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

上例子,这应该是大家都很熟悉耳熟能详的代码, 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. [atAGC006D]Median Pyramid Hard

    二分答案,考虑答案是否会大于等于这个mid,显然所有数值分为两类:大于等于mid和小于mid将n个数转化为01串,如果0和1不相邻,那么答案就是第一个数/最后一个数(一定会相同),考虑有连续两个0/1 ...

  2. [bzoj1572]工作安排

    按照Di排序,从小到大枚举物品,考虑如果直接能选就选上,不能选就考虑替换之前价值最小的来选(显然一定是可行的,只需要在原来选价值最小的时候选这个就行了),这个东西用堆来维护即可 1 #include& ...

  3. 听说你想把对象存储当 HDFS 用,我们这里有个方案...

    传统的大数据集群往往采用本地中心化的计算和存储集群.比如在谷歌早期的[三驾马车]中,使用 GFS 进行海量网页数据存储,用 BigTable 作为数据库并为上层提供各种数据发现的能力,同时用 MapR ...

  4. 【golang必备算法】 Letecode 146. LRU 缓存机制

    力扣链接:146. LRU 缓存机制 思路:哈希表 + 双向链表 为什么必须要用双向链表? 因为我们需要删除操作.删除一个节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持 ...

  5. patch增量更新

  6. CentOS编译openjdk

    编译openjdk 1. 下载openjdk源码 openjdk的官网是OpenJDK (java.net) 在网站左侧就能看到它的源码位置的链接 从图上可以看到,它的源码在两个位置有托管,Mercu ...

  7. 洛谷 P7324 - [WC2021] 表达式求值(状压+dp)

    题面传送门 现场人傻系列-- 首先建出 \(E\) 的表达式树,具体来说表达式的每一个叶子节点表示一个数组 \(A_i\),每一个非叶子节点都表示一次运算,它的值表示左右儿子进行该运算后得到的结果.这 ...

  8. TP、PHP同域不同子级域名共享Session、单点登录

    TP.PHP同域不同子级域名共享Session.单点登录 目的: 为了部署同个域名下不同子级域名共享会话,从而实现单点登录的问题,一处登录,同域处处子系统即可以实现自动登录. PHP支持通过设置coo ...

  9. python-django-数据查询条件

    查询用户的状态是2或者是4的情况 空值和空字符串是不一样的东西!!! 需要注意的是: 项目setting.py里面的时区采用的是美国的时区,我们不要使用这个时区 使用这个时区的,我们输入的日期会进行转 ...

  10. eggNOG 5.0数据库介绍

    目录 1. eggNOG简介 2. eggNOG-Mapper注释原理 3. eggNOG 5.0数据资源 4. eggNOG-Mapper使用 5. NOG.KOG.COG.KEGG.GO区别? 1 ...