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

上例子,这应该是大家都很熟悉耳熟能详的代码, 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. [cf1491H]Yuezheng Ling and Dynamic Tree

    将其按照区间分块(即$[(i-1)K+1,iK]$作为一个块),并定义$f_{x}$表示$x$的祖先中编号最小且与$x$在同一个块内的节点,$f_{x}$可以通过$f_{a_{x}}$转移,即$f_{ ...

  2. 类的访问权限和Object

    1.访问控制权限 1.1.访问控制权限都有哪些? 4个. private 私有 public 公开 protected 受保护 默认 1.2.以上的4个访问控制权限:控制的范围是什么? private ...

  3. 【SpringBoot】(1)-- 基于eclipse配置springboot开发环境

    基于eclipse配置springboot开发环境 1. 下载并配置eclipse ① 前往eclipse官网 https://www.eclipse.org/downloads/packages/ ...

  4. javascript-初级-day01-属性操作、图片切换、短信发送模拟

    大多数js就是操作一些css和html的技巧,如果你会html和css学习js更加轻松哦! js中如何获取元素: 通过id名称来获取元素; document get element by id 'li ...

  5. Codeforces 1067E - Random Forest Rank(找性质+树形 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 一道不知道能不能算上自己 AC 的 D1E(?) 挺有意思的结论题,结论倒是自己猜出来了,可根本不会证( 开始搬运题解 ing: 碰到这样 ...

  6. Codeforces 547E - Mike and Friends(AC 自动机+树状数组)

    题面传送门 好久每做过 AC 自动机的题了--做几个题回忆一下罢 AC 自动机能够解决多串匹配问题,注意是匹配,碰到前后缀的问题那多半不在 AC 自动机能解决的范围内. 在初学 AC 自动机的时候相信 ...

  7. Atcoder Grand Contest 034 F - RNG and XOR(FWT)

    Atcoder 题面传送门 & 洛谷题面传送门 tsc 考试前 A 的题了,结果到现在才写这篇题解--为了 2mol 我已经一周没碰键盘了,现在 2mol 结束算是可以短暂的春天 短暂地卷一会 ...

  8. Yii2 源码分析 入口文件执行流程

    Yii2 源码分析  入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...

  9. Python Cheatsheet

    Comprehensive Python Cheatsheet Download text file, Buy PDF, Fork me on GitHub or Check out FAQ. Con ...

  10. MAC下如何连接安卓(小米)手机进行互传文件?

    命令行: brew cask install android-file-transfer AndroidFileTransfer, 在andorid设备和您的mac电脑之间浏览和传输文件: 不论通过什 ...