闲话不多说,面向对象编程是高级语言的一个特点,但是把它概括成面向抽象更容易直击灵魂,经过了菜鸟大家都要面对的是不要写这么菜的代码了。

上例子,这应该是大家都很熟悉耳熟能详的代码, so easy。

 1 using System;
2 using System.Diagnostics;
3
4 namespace ConsoleApp1
5 {
6 internal class Program
7 {
8 static void Main(string[] args)
9 {
10 Demo demo = new Demo();
11 demo.PrintData();
12 }
13 }
14 internal class Demo
15 {
16 private const int Max = 10;
17 private int[] Generate()
18 {
19 Random rnd = new Random();
20 int[] data = new int[Max];
21 for (int i = 0; i < Max; i++)
22 {
23 data[i] = rnd.Next() % 1023;
24 }
25 return data;
26 }
27 public void PrintData()
28 {
29 string result = string.Join(",", Array.ConvertAll<int, string>(Generate(), n => Convert.ToString(n)));
30 Trace.WriteLine(result);
31 Console.WriteLine(result);
32 }
33 }
34 }

我们看看它的脆弱性在哪里?

•随机数发生器可能变成从数据库提取的一批商品数量或从多个下游企业发来的报文中筛选出来的RFID过检(通过检查)集装箱件数。

•用户可能还需要把信息写入数据库、写入文件,或者觉得Event Viewer显示没什么用处,只要Output窗口就可以了。

归纳一下,这种混合方式的程序相对脆弱,因为会导致变化的因素比较多,按照我们之前设计模式的经验,这时候应该抽象对象,这里我们先把V和M抽象出来,然后在C中组合它们:

using System;
using System.Collections.Generic;
using System.Diagnostics; namespace ConsoleApp2
{
internal class Program
{
static void Main(string[] args)
{
Controller controller = new Controller();
controller.Model = new Randomizer();
controller += new TraceView();
controller += new ConsoleView();
controller.Process();
}
}
public class Controller
{
private IList<IView> views = new List<IView>(); private IModel model;
public virtual IModel Model { get => model; set => model = value; } public void Process()
{
if (views.Count == 0) return;
string result = string.Join(",", Array.ConvertAll<int, string>(model.Data, n => Convert.ToString(n)));
foreach (var view in views)
{
view.Print(result);
}
}
public static Controller operator +(Controller controller, IView view)
{
if (view == null) throw new ArgumentNullException("view");
controller.views.Add(view);
return controller;
}
public static Controller operator -(Controller controller, IView view)
{
if (view == null) throw new ArgumentNullException("view");
controller.views.Remove(view);
return controller;
}
} class Randomizer : IModel
{
public int[] Data
{
get
{
Random random = new Random();
int[] result = new int[10];
for (int i = 0; i < result.Length; i++)
{
result[i] = random.Next() % 1023;
}
return result;
}
}
}
class ConsoleView : IView
{
public void Print(string data)
{
Console.WriteLine(data);
}
} class TraceView : IView
{
public void Print(string data)
{
Trace.WriteLine(data);
}
} public interface IView
{
void Print(string data);
} public interface IModel
{
int[] Data { get; }
}
}

按照上面的介绍,主动方式MVC需要一个观察者对M保持关注。这里我们简单采用.NET的事件机制来充当这个角色,而本应从V发起的重新访问M获得新数据的过程也简化为事件参数,在触发事件的同时一并提供,代码如下所示:

using System;
using System.Diagnostics; namespace ConsoleApp3
{
internal class Program
{
static void Main(string[] args)
{
Controller controller = new Controller();
IModel model = new Model();
controller += new TraceView();
controller += new ConsoleView();
// 后续Model修改时,不经过Controller,而是经由观察者完成View的变化 model[1] = 2000; // 第一次修改(修改的内容按照之前的随机数计算不会出现) model[3] = -100; // 第二次修改(修改的内容按照之前的随机数计算不会出现) }
} internal class Controller
{
private IModel model;
public virtual IModel Model { get => model; set => model = value; }
public static Controller operator +(Controller controller, IView view)
{
if (view == null) throw new ArgumentNullException(nameof(view));
controller.Model.DataChanged += view.Handler;
return controller;
}
public static Controller operator -(Controller controller, IView view)
{
if (view == null) throw new ArgumentNullException(nameof(view));
controller.Model.DataChanged -= view.Handler;
return controller;
}
} internal class Model : IModel
{
public event EventHandler<ModelEventArgs> DataChanged;
private int[] data;
public int this[int index]
{
get => data[index];
set
{
this.data[index] = value;
DataChanged?.Invoke(this, new ModelEventArgs(data));
}
}
public Model()
{
Random rnd = new Random();
data = new int[10];
for (int i = 0; i < data.Length; i++)
{
data[i] = rnd.Next() % 1023;
}
}
}
internal abstract class ViewBase : IView
{
public abstract void Print(string data);
public virtual void OnDataChanged(object sender, ModelEventArgs args)
{
Print(args.Context);
}
public virtual EventHandler<ModelEventArgs> Handler
{
get => this.OnDataChanged;
}
}
internal class TraceView : ViewBase
{
public override void Print(string data)
{
Trace.WriteLine(data);
} }
internal class ConsoleView : ViewBase
{
public override void Print(string data)
{
Console.WriteLine(data);
}
}
internal class ModelEventArgs : EventArgs
{
private string content;
public string Context { get => this.content; } public ModelEventArgs(int[] data)
{
content = string.Join(",", Array.ConvertAll<int, string>(data, n => Convert.ToString(n)));
}
}
internal interface IModel
{
event EventHandler<ModelEventArgs> DataChanged;
int this[int index] { get; set; }
}
internal interface IView
{
EventHandler<ModelEventArgs> Handler { get; }
void Print(string data);
} }

从上面的示例不难看出,相对被动方式的MVC而言,采用.NET事件方式实现主动方式有下述优势:

•结构更加松散耦合,M/V之间可以没有直接的依赖关系,组装过程可以由C完成,M/V之间的观察者仅经由.NET标准事件和委托进行交互。

•不用设计独立的观察者对象。

•由于C不需参与M数据变更后实际的交互过程,因此C也无需设计用来保存V的容器。

•如果EventArgs设计合理的话,可以更自由地与其他产品或第三方对象体系进行集成。

注: 次源于 王翔 《设计模式 基于c#的工程化实现及扩展》 摘自其中一节,2008出版的版本。新的版本改动很大,没有那么喜欢了。推荐有兴趣的自行剁手,一顿饭的钱。这本书的质量本人觉得非常高,虽然2008的年出版,但是放到现在再结合现代的技术发展更加印证了这本书是经得起时间考验的。

C#面对抽象编程第一讲的更多相关文章

  1. C#面向抽象编程第二讲

    抽象编程怎么说呢,以观察者模式为例: 观察者模式有两个对象,一个是观察者,一个是可观察者(字面翻译很别扭observable),消息发布者(提供者). 第一层如下,三个对象A.B.C分别有一个接收消息 ...

  2. 1. Shell编程第一讲

    (1)shell 历史: Shell的作用是解释执行用户的命令,用户输入一条命令,Shell就解释执行一条,这种方式称为交互式(Interactive). Shell还有一种执行命令的方式称为批处理( ...

  3. Python学习6——再谈抽象(面对对象编程)

    1.对象魔法 在面对对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法. 使用对象而非全局变量以及函数的原因有多个,而最重要的好处不过以下几点: 多态:可对不同类型的对象 ...

  4. MFC控件第一讲.DC编程

    MFC控件第一讲.DC编程 一丶简介 什么是DC,DC有什么用. DC成为设备描述符表. DC的作用就是可以进行绘制. 比如我们的窗口都是绘制出来的.  DC可以简单理解为.没一个窗口程序都有一块内存 ...

  5. 少儿编程Scratch第一讲:Scratch完美的初体验

    素材及视频下载 链接:https://pan.baidu.com/s/1qX0T2B_zczcLaCCpiRrsnA提取码:xfp8 都说未来是人工智能.计算机程式控制的时代,如何让青少年接触计算机编 ...

  6. 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  7. java--面向抽象编程

    所谓面向抽象编程是指当设计某种重要的类时,不让该类面向具体的类,而是面向抽象类,及所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象.就是利用abstract来设计实现用户需求. 比如:我 ...

  8. 逆向实用干货分享,Hook技术第一讲,之Hook Windows API

    逆向实用干货分享,Hook技术第一讲,之Hook Windows API 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) ...

  9. 面对对象编程(OOP, Object Oriented Programming)及其三个基本特性

    一千个读者,一千个哈姆雷特.对于面对对象编程,书上都会告诉我们它有三个基本特性,封装,继承,多态,但谈起对这三点的见解,又是仁者见仁智者见智,感觉还是得多去编程中体验把 . 面向对象编程(OOP, O ...

随机推荐

  1. [loj3500]矩阵游戏

    为了方便,令$a_{i,j}$的下标范围为$[0,n]$和$[0,m]$,$b_{i,j}$的下标范围为$[1,n]$和$[1,m]$ 当确定$a_{i,0}$和$a_{0,j}$后,即可通过$b_{ ...

  2. [cf582E]Boolean Function

    由于每一个运算都有括号,因此添加的运算不会改变运算顺序 先将其建出一棵表达式树,也就是维护两个栈,是节点和运算符优先级单调递增的栈(设置左括号优先级最低,右括号弹出直至左括号) 每一次运算,也就是新建 ...

  3. [atARC075F]Mirrored

    假设$n=\sum_{i=0}^{k}a_{i}10^{i}$(其中$a_{k}>0$),则有$d=f(n)-n=\sum_{i=0}^{k}(10^{k-i}-10^{i})a_{i}$,考虑 ...

  4. [Net 6 AspNetCore Bug] 解决返回IAsyncEnumerable<T>类型时抛出的OperationCanceledException会被AspNetCore 框架吞掉的Bug

    记录一个我认为是Net6 Aspnetcore 框架的一个Bug Bug描述 在 Net6 的apsnecore项目中, 如果我们(满足以下所有条件) api的返回类型是IAsyncEnumerabl ...

  5. html+css第二篇

    <img src="图片地址" alt="图片名"/> 图片(单标签)alt属性 是图片名字,是给百度搜索引擎抓取使用: a标签: 链接/下载/锚点 ...

  6. .Net Core中使用ElasticSearch(二)

    .Net的ElasticSearch 有两个版本,Elasticsearch.Net(低级) 和 NEST(高级),推荐使用 NEST,低级版本的更灵活,水太深 把握不住.有个需要注意,使用的版本号必 ...

  7. Codeforces 1383C - String Transformation 2(找性质+状压 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神奇的强迫症效应,一场只要 AC 了 A.B.D.E.F,就一定会把 C 补掉( 感觉这个 C 难度比 D 难度高啊-- 首先考虑对问题进 ...

  8. [linux] 大批量删除任务

    一不小心投了巨多任务,或者投递的资源不合理时,想批量杀掉这些任务. kill的方法就不说了,我这里用qdel的方法. 用了这么一条命令: qstat |sed '1,2d' |awk -F' ' '{ ...

  9. jquery chosen onchange 值改变时触发方法

    jquery chosen onchange 值改变时触发方法如下:$(".chzn-select").chosen().on("change", functi ...

  10. 单元测试在Unity中的应用

    项目描述:简单演示单元测试在Unity中的应用 项目地址:UnityTestRunner_Tutorial - SouthBegonia 项目版本:2020.3.20f1 项目用法:打开就用,代码都放 ...