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

上例子,这应该是大家都很熟悉耳熟能详的代码, 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. Study Blazor .NET(一)简介

    翻译自:Study Blazor .NET,转载请注明. 介绍 Blazor是一个全新的 Web UI 框架,它使用c# .Razor 和 HTML以及 WebAssembly W3C标准.它提供了用 ...

  2. 基于 Docker 安装 RocketMQ

    docker-compose.yml version: '3.5' services: rmqnamesrv: image: foxiswho/rocketmq:server container_na ...

  3. 如何基于 React 封装一个组件

    如何基于 React 封装一个组件 前言 很多小伙伴在第一次尝试封装组件时会和我一样碰到许多问题,比如人家的组件会有 color 属性,我们在使用组件时传入组件文档中说明的属性值如 primary , ...

  4. Java计算器的简易实现(+-*/)

    java计算器的简易实现(+-*/) import java.util.Scanner; /* 写一个计算器 实现加减乘除四个功能 并且能够用循环接收新的数据,通过用户交互实现 写四个方法 + - * ...

  5. 详解在Linux中安装配置MongoDB

    最近在整理自己私人服务器上的各种阿猫阿狗,正好就顺手详细记录一下清理之后重装的步骤,今天先写点数据库的内容,关于在Linux中安装配置MongoDB 说实话为什么会装MongoDB呢,因为之前因为公司 ...

  6. Codeforces 1446D2 - Frequency Problem (Hard Version)(根分)

    Codeforces 题面传送门 & 洛谷题面传送门 人菜结论题做不动/kk 首先考虑此题一个非常关键的结论:我们设整个数列的众数为 \(G\),那么在最优子段中,\(G\) 一定是该子段的众 ...

  7. 洛谷 P5540 - [BalkanOI2011] timeismoney | 最小乘积生成树(最小生成树)

    洛谷题面传送门 大概是一个比较 trivial 的小 trick?学过了就不要忘了哦( 莫名奇妙地想到了 yyq 的"hot tea 不常有,做过了就不能再错过了" 首先看到这种二 ...

  8. 2021-2-3-利用anaconda+prefetch+aspera从NCBI的SRA数据库中下载原始测序数据

    目录 1.Conda连接不上镜像源问题 2. aspera不能再独立使用 3.使用prefetch搭配aspera 4. prefetch下载方法 记录下下载过程,为自己和后人避坑. 1.Conda连 ...

  9. MYSQL5.8-----4

    cc

  10. git放弃修改,强制覆盖本地代码

    1.git fetch --all  //从远程拉取最新的代码 不merge 2.git reset --hard origin/develop  //使用指定分支的代码(此处develop)强制覆盖 ...