前言:前篇 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模板代码

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <t ...

  2. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q99-Q101)

    Question  99 You have designed a new SharePoint 2010 Web Part that was deployed to the testing envir ...

  3. Android开发学习——android体系结构

    Android的体系结构采用了分层架构的思想, 从上层到底层共包括四层,分别是应用程序程序层.应用框架层.系统库和Android运行时和Linux内核. 一 应用程序层 该层提供一些核心应用程序包,例 ...

  4. lable计算行高

    _introduce.text=status.introduce; //设置行间距 NSMutableAttributedString *attributedString = [[NSMutableA ...

  5. MongoDB学习笔记~客户端命令行的使用

    回到目录 当我们从MongoDB网站下载安装包之后,它会伴随有一系列的工具,服务器程序mongod是我们耳熟能详的了,客户端mongo和性能检测mongostat我们可能就没有用过了,今天主要是介绍一 ...

  6. BCP笔记整理(二)

    BCP的基础用法可以参考上一篇:http://www.cnblogs.com/Gin-23333/p/5489889.html 这篇是补充一些有可能会用到,但是出场几率并不算大的几个参数 1.首先是 ...

  7. C++基础——函数指针 函数指针数组

    ==================================声明================================== 本文版权归作者所有. 本文原创,转载必须在正文中显要地注明 ...

  8. oracle树形查询 start with connect by

    一.简介 在oracle中start with connect by (prior) 用来对树形结构的数据进行查询.其中start with conditon 给出的是数据搜索范围, connect ...

  9. Linux Daemon进程

    模型 创建孤儿进程 fork() 创建新的会话 setid() 改变当前目录为根目录chdir() 重设文件权限掩码umask() 关闭文件描述符close() 创建孤儿进程 创建孤儿进程只需要将父进 ...

  10. 服务器文件上传下载(XShell+Xftp)

    1.下载XShell安装包+Xftp安装包.百度网盘(XShell):https://pan.baidu.com/s/1eR4PFpS 百度网盘(Xftp):https://pan.baidu.com ...