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

上例子,这应该是大家都很熟悉耳熟能详的代码, 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. Redis | 第一部分:数据结构与对象 下篇《Redis设计与实现》

    目录 前言 1. Redis对象概述 1.1 对象的定义 2. 字符串对象 3. 列表对象 3.1 quicklist 快速链表 4. 哈希对象 5. 集合对象 6. 有序集合对象 7. Redis对 ...

  2. 谱文混排之lilypond-book

    Lilypond有自带的谱文混排的工具lilypond-book,但是作为外行,一直很难搞清楚这个操作是怎样做的.很久之前请教过别人,但介于我的个人能力,只有粗浅理解,操作不得要领.在许多信息的拼凑之 ...

  3. JavaWeb 请求转发重定向

    请求转发和重定向 request除了可以作为请求对象之外,还可以作为域对象,但是该域对象的取值范围,是一次请求范围之内(浏览器地址栏没有发生跳转访问别的资源) 作用:将servlet中的数据通过req ...

  4. P6072 『MdOI R1』Path

    考虑我们有这样操作. 我们只要维护两点在子树内和两点在子树外的异或和即可. 前者可以类似于线段树合并的trie树合并. 后者有两种做法: 一种是把dfn序翻倍:然后子树补变成了一个区间最大异或问题,可 ...

  5. PostgreSQL 数据库备份与还原

    PostgreSQL 数据库备份与还原 目录 备份 还原 栗子 备份 PostgreSQL提供的一个工具pg_dump,逻辑导出数据,生成sql文件或其他格式文件,pg_dump是一个客户端工具,可以 ...

  6. [R]在dplyr基础上编写函数-(1)eval

    tidyverse系列的R包虽然解放了大家的双手,但同时也束缚了我们重新编写函数的能力.在这一套语法中,要实现作为函数参数的字符串和变量之间的相互转换困难重重,但只要掌握了其中原理后,也就能够游刃有余 ...

  7. 【Perl示例】整合多个文件

    这个需求是在生信分析中几乎天天用到,各种语言都能实现,也都各有特点.这次以perl为例. 已知 文件CT-VS-CON.All.xls为全部蛋白表达矩阵及其差异分析结果. 文件Homo_sapiens ...

  8. python-django-模板标签

    注意:这个控制语句和python的差不多,但是记住必须有endfor 和endif 结尾 模板文件的django格式的注释是不会出现再网页渲染的源代码当中的 使用列子: <!DOCTYPE ht ...

  9. jenkins原理简析

    持续集成Continuous Integration(CI) 原理图: Gitlab作为git server.Gitlab的功能和Github差不多,但是是开源的,可以用来搭建私有git server ...

  10. 爬虫动态渲染页面爬取之selenium驱动chrome浏览器的使用

    Selenium是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样,可以用其进行网页动态渲染页面的爬取. 支持的浏览器包括IE(7, 8, 9, 10 ...