前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法。这篇打算从设计模式的角度去解析下委托的使用。我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性。上次说过,方法可以理解为委托的实例,站在方法的层面,委托实例的一个非常有用的特性是它既不知道,也不关心其封装方法所属类的详细信息,对它来说最重要的是这些方法与该委托的参数和返回值的兼容性。即只要方法的返回类型和参数表是相同的,则方法与委托类型兼容,方法的名称及方法所属类等信息委托是不关心的。有一定编程经验的大侠们肯定都接触过设计模式,其实设计模式大多数都是面向对象多态特性的体现,通过重写子类方法去展现不同的设计需求,这样看,既然是方法重写,那么方法的参数类型和返回值类型肯定是一致的,这是不是和委托的实例十分相似,这样说来,我们通过多态去实现的设计模式是否可以用委托的形式去代替。博主觉得,为了更好的理解委托,可以从这方面着手试试。。。

  时间过得真快,转眼C#基础系列已经写了8篇随笔了,不管写的好不好,博主都会继续,做事要有始有终嘛~~前天在园子看到一篇文章目录的博文,这里将博主的系列文章也列一个目录出来,这样以后找起来也方便。

  此篇简单抽取了几个设计模式分别按照多态和委托的方式去实现,当然这里的重点并不是讲设计模式,而是为了使读者更好地理解委托。所以设计模式的很多细节,本篇可能会略过。

一、简单工厂模式:本篇就借助计算器的例子加以说明。

1、多态实现简单工厂模式。

   class Program2
{
static void Main(string[] args)
{
//1.使用多态实现简单工厂模式
int x = , y = ;
var iRes1 = GetObject("+").Compute(x, y);
var iRes2 = GetObject("-").Compute(x, y);
var iRes3 = GetObject("*").Compute(x, y);
var iRes4 = GetObject("/").Compute(x, y); Console.WriteLine(iRes1);
Console.WriteLine(iRes2);
Console.WriteLine(iRes3);
Console.WriteLine(iRes4); Console.ReadKey();
} static Calculator GetObject(string type)
{
Calculator oRes = null;
switch (type)
{
case "+":
oRes = new Add();
break;
case "-":
oRes = new Subtract();
break;
case "*":
oRes = new Multiply();
break;
case "/":
oRes = new Divide();
break;
}
return oRes;
}
} public class Calculator
{
public virtual int Compute(int x, int y)
{
return ;
}
} public class Add : Calculator
{
public override int Compute(int x, int y)
{
return x + y;
}
} public class Subtract : Calculator
{
public override int Compute(int x, int y)
{
return x - y;
}
} public class Multiply : Calculator
{
public override int Compute(int x, int y)
{
return x * y;
}
} public class Divide : Calculator
{
public override int Compute(int x, int y)
{
if (y == )
{
return ;
}
return x / y;
}
}

代码应该很容易看懂,直接通过方法的重写去实现,在此就不过多讲解。

2、委托方式实现简单工厂模式。

class Program2
{ static void Main(string[] args)
{
#region 2.委托实现简单工厂模式
int x = , y = ;
var oCalculator = new Calculator();
var iRes1 = oCalculator.Compute(x, y, oCalculator.Add);//将方法作为参数传下去
var iRes2 = oCalculator.Compute(x, y, oCalculator.Subtract);
var iRes3 = oCalculator.Compute(x, y, oCalculator.Multiply);
var iRes4 = oCalculator.Compute(x, y, oCalculator.Divide); Console.WriteLine(iRes1);
Console.WriteLine(iRes2);
Console.WriteLine(iRes3);
Console.WriteLine(iRes4);
#endregion Console.ReadKey();
}
} public delegate int DelegateCalculator(int x, int y); public class Calculator
{
     //将方法的实例传递进来,在Compute方法里面执行
public int Compute(int x, int y, DelegateCalculator calculator)
{
return calculator(x, y);
} public int Add(int x, int y)
{
return x + y;
} public int Subtract(int x, int y)
{
return x - y;
} public int Multiply(int x, int y)
{
return x * y;
} public int Divide(int x, int y)
{
if (y == )
{
return ;
}
return x / y;
}
}

这里需要定义四个实现方法Add、Subtract、Multiply、Divide,而不用在意这四个方法在哪个类下面,只要这四个方法的的参数和返回值和委托的定义保持一致即可。这也验证了上面说的 “站在方法的层面,委托实例的一个非常有用的特性是它既不知道,也不关心其封装方法所属类的详细信息,对它来说最重要的是这些方法与该委托的参数和返回值的兼容性” 。两种方式得到的结果是相同的:

二、观察者模式:观察者模式最典型的场景就是订阅者和订阅号的场景

1、纯多态方式实现观察者模式:这种代码园子里面非常多。

class Program3
{
static void Main(string[] args)
{
// 具体主题角色通常用具体自来来实现
ConcreteSubject subject = new ConcreteSubject(); subject.Attach(new ConcreteObserver(subject, "Observer A"));
subject.Attach(new ConcreteObserver(subject, "Observer B"));
subject.Attach(new ConcreteObserver(subject, "Observer C")); subject.SubjectState = "Ready";
subject.Notify(); Console.Read();
}
} //抽象主题类
public abstract class Subject
{
private IList<Observer> observers = new List<Observer>(); /// <summary>
/// 增加观察者
/// </summary>
/// <param name="observer"></param>
public void Attach(Observer observer)
{
observers.Add(observer);
} /// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void Detach(Observer observer)
{
observers.Remove(observer);
} /// <summary>
/// 向观察者(们)发出通知
/// </summary>
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
} //具体主题类
public class ConcreteSubject : Subject
{
private string subjectState; /// <summary>
/// 具体观察者的状态
/// </summary>
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
} //抽象观察者类
public abstract class Observer
{
public abstract void Update();
} //具体观察者
public class ConcreteObserver : Observer
{
private string observerState;
private string name;
private ConcreteSubject subject; /// <summary>
/// 具体观察者用一个具体主题来实现
/// </summary>
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
} public ConcreteObserver(ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
} /// <summary>
/// 实现抽象观察者中的更新操作
/// </summary>
public override void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
}
}

可以看到虽然已经很好的实现了观察者Observer 和主题Subject之间的分离。但是Subject的内部还是有对观察者的调用:

public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}

2、多态和委托实现观察者模式。

   class Program3
{
static void Main(string[] args)
{
// 具体主题角色通常用具体自来来实现
ConcreteSubject subject = new ConcreteSubject(); //传入的只是观察者的通过方法。
subject.Attach(new ConcreteObserver(subject, "Observer A").Update);
subject.Attach(new ConcreteObserver(subject, "Observer B").Update);
subject.Attach(new ConcreteObserver(subject, "Observer C").Update); subject.SubjectState = "Ready";
subject.Notify(); Console.Read();
}
} public delegate void ObserverDelegate(); //抽象主题类
public abstract class Subject
{
public ObserverDelegate observedelegate; /// <summary>
/// 增加观察者
/// </summary>
/// <param name="observer"></param>
public void Attach(ObserverDelegate observer)
{
observedelegate += observer;
} /// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void Detach(ObserverDelegate observer)
{
observedelegate -= observer;
} /// <summary>
/// 向观察者(们)发出通知
/// </summary>
public void Notify()
{
if (observedelegate != null)
{
observedelegate();
}
}
} //具体主题类
public class ConcreteSubject : Subject
{
private string subjectState; /// <summary>
/// 具体观察者的状态
/// </summary>
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
} //具体观察者
public class ConcreteObserver
{
private string observerState;
private string name;
private ConcreteSubject subject; /// <summary>
/// 具体观察者用一个具体主题来实现
/// </summary>
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
} public ConcreteObserver(ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
} /// <summary>
/// 实现抽象观察者中的更新操作
/// </summary>
public void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
}
}

得到结果:

这样设计的优势:

(1)将通知的方法Update通过委托的形式传入主题对象。这样主题对象Subject就完全和观察者隔离。更好地实现了低耦合。

(2)减少了观察者抽象类的定义。使整个设计更加精简。

(3)如果将设计更进一步,观察者这边自定义delegate void ObserverDelegate()这种类型的方法。比如需要执行Update()方法之后还要记录一个日志的操作。如:

//具体观察者
public class ConcreteObserver
{
private string observerState;
private string name;
private ConcreteSubject subject; /// <summary>
/// 具体观察者用一个具体主题来实现
/// </summary>
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
} public ConcreteObserver(ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
} /// <summary>
/// 实现抽象观察者中的更新操作
/// </summary>
public void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
} public void Log()
{
Console.WriteLine("Log:Update方法执行完成");
}
}

那么在客户端调用时只需要将Log方法以委托的形式传入即可:

static void Main(string[] args)
{
// 具体主题角色通常用具体自来来实现
ConcreteSubject subject = new ConcreteSubject(); //传入的只是观察者的通过方法。
var obj = new ConcreteObserver(subject, "Observer A");
subject.Attach(obj.Update);
subject.Attach(obj.Log); subject.SubjectState = "Ready";
subject.Notify(); Console.Read();
}

是不是显得更灵活一点。如果是纯多态的方式,由于Subject里面指定了调用Update()方法,所以当需要增加Log方法的时候代码的改变量要大。

三、模板方法模式,这里就以设备采集为例来进行说明:

1、多态实现模板方法模式:

    class Program4
{
static void Main(string[] args)
{
var oTem1 = new DeviceMML();
oTem1.Spider();
Console.WriteLine("");
var oTem2 = new DeviceTL2();
oTem2.Spider(); Console.ReadKey();
}
} public abstract class TempleteDevice
{
// 模板方法,不要把模版方法定义为Virtual或abstract方法,避免被子类重写,防止更改流程的执行顺序
public void Spider()
{
Console.WriteLine("设备采集开始");
this.Login();
this.Validation();
this.SpiderByType1();
this.SpiderByType2();
this.LoginOut(); Console.WriteLine("设备采集结束");
} // 登陆
public void Login()
{
Console.WriteLine("登陆");
} // 验证
public void Validation()
{
Console.WriteLine("验证");
} // 采集
public abstract void SpiderByType1();
public abstract void SpiderByType2(); // 注销
public void LoginOut()
{
Console.WriteLine("注销");
}
} //MML类型的设备的采集
public class DeviceMML : TempleteDevice
{
public override void SpiderByType1()
{
Console.WriteLine("MML类型设备开始采集1");
//.......
} public override void SpiderByType2()
{
Console.WriteLine("MML类型设备开始采集2");
}
} //TL2类型设备的采集
public class DeviceTL2 : TempleteDevice
{
public override void SpiderByType1()
{
Console.WriteLine("TL2类型设备开始采集1");
//.......
} public override void SpiderByType2()
{
Console.WriteLine("TL2类型设备开始采集2");
}
}

父类里面的非abstract方法都是模板方法,也就是子类公用并且不可以重写的方法。SpiderType1和SpiderType2是需要子类重写的方法。模板方法模式在抽象类中定义了算法的实现步骤,将这些步骤的实现延迟到具体子类中去实现,从而使所有子类复用了父类的代码,所以模板方法模式是基于继承的一种实现代码复用的技术。

2、使用委托改写后:

    class Program4
{
static void Main(string[] args)
{
var oTem1 = new TempleteDevice(DeviceMML.SpiderByType1, DeviceMML.SpiderByType2);
oTem1.Spider(); Console.WriteLine(""); var oTem2 = new TempleteDevice(DeviceTL2.SpiderByType1, DeviceTL2.SpiderByType2);
oTem2.Spider();
Console.ReadLine();
}
} public delegate void DeviceDelegate(); public class TempleteDevice
{
public DeviceDelegate oDelegate; public TempleteDevice(params DeviceDelegate[] lstFunc)
{
foreach (var oFunc in lstFunc)
{
oDelegate += oFunc;
} } // 模板方法,不要把模版方法定义为Virtual或abstract方法,避免被子类重写,防止更改流程的执行顺序
public void Spider()
{
Console.WriteLine("设备采集开始");
this.Login();
this.Validation();
if (oDelegate != null)
{
oDelegate();
}
this.LoginOut(); Console.WriteLine("设备采集结束");
} // 登陆
public void Login()
{
Console.WriteLine("登陆");
} // 验证
public void Validation()
{
Console.WriteLine("验证");
} // 注销
public void LoginOut()
{
Console.WriteLine("注销");
}
} //MML类型的设备的采集
public class DeviceMML
{
public static void SpiderByType1()
{
Console.WriteLine("MML类型设备开始采集1");
//.......
} public static void SpiderByType2()
{
Console.WriteLine("MML类型设备开始采集2");
}
} //TL2类型设备的采集
public class DeviceTL2
{
public static void SpiderByType1()
{
Console.WriteLine("TL2类型设备开始采集1");
//.......
} public static void SpiderByType2()
{
Console.WriteLine("TL2类型设备开始采集2");
}
}

得到结果:

优化模板方法模式的意义:

(1)解除了子类和父类之间的继承关系,更好地实现了对象间的低耦合。

(2)采用委托可以动态实现方法的组合,这种方式更加灵活,子类可以更加灵活的设计不同部分的方法。然后方法的数量通过params来传递,方法的数量没有什么严格的限制。

当然其他设计模式也可以使用委托去优化设计,博主在这里就暂时只分享这三种模式的异同。总的来说,委托不可能代替多态去实现各种模式,但是它和多态联合起来使用可以实现更加灵活的设计。通过这两篇下来,不知道你是否对委托有点感觉了呢,委托这东西,重在实战,就像游泳一样,如果不用那么几次,你永远也不可能学会。以上只是博主个人的理解,可能很多方便没有考虑得那么全面,希望各位园友拍砖斧正~~

C#基础系列——委托和设计模式(二)的更多相关文章

  1. C#基础系列——委托实现简单设计模式

    前言:上一篇介绍了下多线程的相关知识:C#基础系列——多线程的常见用法详解,里面就提到了委托变量.这篇简单介绍下委托的使用.当然啦,园子里面很多介绍委托的文章都会说道:委托和事件的概念就像一道坎,过了 ...

  2. Flutter基础系列之混合开发(二)

    1.混合开发的场景 1.1作为独立页面加入 这是以页面级作为独立的模块加入,而不是页面的某个元素. 原生页面可以打开Flutter页面 Flutter页面可以打开原生页面 1.2作为页面的一部分嵌入 ...

  3. Java基础系列(39)- 二维数组

    多维数组 多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组. 二维数组 int a[][]=new int[2][5]; 解析:以上二维数组a可以看成一个 ...

  4. C#基础系列——再也不用担心面试官问我“事件”了

    前言:作为.Net攻城狮,你面试过程中是否遇到过这样的问题呢:什么是事件?事件和委托的区别?既然事件作为一种特殊的委托,那么它的优势如何体现?诸如此类...你是否也曾经被问到过?你又是否都答出来了呢? ...

  5. C#基础系列——异步编程初探:async和await

    前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...

  6. C#基础系列——一场风花雪月的邂逅:接口和抽象类

    前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的.那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗 ...

  7. c#基础系列(转)

    转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...

  8. 【C++自我精讲】基础系列二 const

    [C++自我精讲]基础系列二 const 0 前言 分三部分:const用法.const和#define比较.const作用. 1 const用法 const常量:const可以用来定义常量,不可改变 ...

  9. Vue基础系列(二)——Vue中的methods属性

      写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家指出. 作者简介: 一个不知名的前端开发 ...

随机推荐

  1. 初识HTML

    前面的话 HTML文档的后缀一般都是.html,但是在以前,.htm后缀也是不少的,它们都代表html文档,实际上也没有本质的区别.htm是在win32时代,系统只能识别3位扩展名时使用的.现在一般都 ...

  2. 学习zepto.js(对象方法)[2]

    今天来说下zepto那一套dom操作方法, prepend,append,prependTo,appendTo,before,after,insertBefore,insertAfter; 按着从内到 ...

  3. 显示快照监控:/SDF/MON

    透过SE38运行程序/SDF/MON,可以显示屏幕的监控快照:

  4. HIVE 在执行大量数据JOIN的时候,容易产生内存不足的情况

    情况 很多时间遇到 "Caused by: java.lang.OutOfMemoryError: Java heap space" Caused by: java.lang.Ou ...

  5. tableview左滑按钮 tableviewcell自定义左滑按钮

    当我们在使用tableview时,往往需要在cell左滑时显示一个或是多个按钮,但系统默认的只可显示一个,如常见的删除按钮,那么当我们的需求要求要有多个按钮时又该怎么办呢,我们往下看. 首先,现看看系 ...

  6. 最新深度技术GHOST XP系统旗舰增强版 V2016年

    来自系统妈:http://www.xitongma.com 深度技术GHOST xp系统旗舰增强版 V2016年 系统概述 深度技术ghost xp系统旗舰增强版集合微软JAVA虚拟机IE插件,增强浏 ...

  7. Mysql数据库的基本概念和架构

    数据库 1.键:主键是表中的标志列.一个键可能由几列组成.可以使用键作为表格之间的引用. CustomerID是Customers表的主键,当它出现在其他表,例如Orders表中的时候就称它为外键. ...

  8. Play Framework 完整实现一个APP(八)

    创建Tag标签 1.创建Model @Entity @Table(name = "blog_tag") public class Tag extends Model impleme ...

  9. 将String转化成Stream,将Stream转换成String

    using System;using System.IO;using System.Text;namespace CSharpConvertString2Stream{     class Progr ...

  10. 查看Linux版本信息

    如何查看Linux系统使用的版本信息呢? 下面这篇文章收集.整理了一些常见的查看Linux系统版本的方法.由于手头只有Oracle Linux.Centos Linux.Redhat Linux三个版 ...