观察者模式及事件与委托的区别-20171215内训会
本文章主要介绍事件与委托的区别以及观察者模式:
1.首先,熟悉c#的人都知道,事件是一种特殊形式的委托,但是说到为什么,应该没有多少人能说清楚道明白,在学校老师的讨论下,我们对事件进行了一个反编译,让我们知道事件到底是如何实现的.
从这个反编译看来,事件其实就是封装了两个使用委托的方法.一个是添加的,一个是移除的.而它最后的运行还是使用的委托.
-----下面我们来试着猜一下为什么已经有了委托微软还要添加事件这个东西-------
首先,委托是可以被覆盖掉的,并且委托可以直接跳过业务流程直接被调用.
/// <summary> /// 这个类是为了模仿事件的业务处理 /// </summary>
public class OldBugManager { /// <summary> /// 发送委托 /// </summary>
/// <param name="name">人名</param> public delegate void SendDelegate(string name); /// <summary> /// 发送事件 /// </summary> public SendDelegate OldSendEvent; /// <summary> /// 监听bug的方法 /// </summary> /// <param name="name"></param> public void OldBugMonitor(string name) { //监听bug开始 //此处省略代码1万行 //发生bug if (OldSendEvent != null) { OldSendEvent(name); } //发送完毕的业务代码开始 //此处省略代码1千行 //发送完毕的业务代码结束 } }
上面的方法可以让我们向委托中存放我们在特定情况下要调用的方法.这样就会出现以下问题
1 EMail email = new EMail();//这个类是发送邮箱的实现类(这里不需要知道里面干了什么事情,只要知道它可以给某一个人发送EMail,提示修改bug) 2 Electric electric = new Electric();//这个类是用来电击提醒的实现类(这里不需要知道里面干了什么事情,只要知道它可以电击某一个人,提示修改bug) 3 string name = "小张";//修改bug的人名 4 #region 优化前
5 OldBugManager oldBugManager = new OldBugManager();//我们来实例化一个我们要调用的事件 6 7 //先来看一看委托为null的时候不能+= 所以要先判断委托是否是null 8 if (oldBugManager.OldSendEvent != null) 9 { 10 oldBugManager.OldSendEvent += new OldBugManager.SendDelegate(email.SendEmail); 11 oldBugManager.OldSendEvent += new OldBugManager.SendDelegate(electric.ElectricExec); 12 } 13 else 14 { 15 oldBugManager.OldSendEvent = new OldBugManager.SendDelegate(email.SendEmail);//添加一个发送邮件的委托 16 oldBugManager.OldSendEvent += new OldBugManager.SendDelegate(electric.ElectricExec);//添加一个电击的委托 17 } 18 #region 引发的问题 19 //这样做引发的问题 开始 20 oldBugManager.OldSendEvent = new OldBugManager.SendDelegate(MakeTrouble); 21 //1 因为委托暴露所以可以在外部重写 22 oldBugManager.OldSendEvent.Invoke(name);
23 // 2 我可以跳过流程直接执行,因为我们的监听方法是OldBugMonitor,这样就跳过了业务流程直接运行 24 //结束
25 #endregion 26 oldBugManager.OldBugMonitor(name); //监听bug方法 27 #endregion
-------下面我们来编写事件的使用方式---------
public class BugManager { /// <summary> /// 发送委托 /// </summary> public delegate void SendDelegate(); /// <summary> /// 发送事件 /// </summary> public event SendDelegate SendEvent; /// <summary> /// 监听bug的方法 /// </summary> public void BugMonitor() { //监听bug开始 //此处省略代码1万行 //发生bug if (SendEvent != null) { SendEvent(); } //发送完毕的业务代码开始 //此处省略代码1千行 //发送完毕的业务代码结束 } }
下面是调用事件
BugManager bugManage = new BugManager(); EMail eMail = new EMail();
bugManage.SendEvent += new BugManager.SendDelegate(eMail.SendEmail); //将发送邮件与bug监听绑定 Electric electric = new Electric(); bugManage.SendEvent += new BugManager.SendDelegate(electric.ElectricExec);//将电击与bug监听绑定 bugManage.BugMonitor(); //触发事件,这样的话就不存在跳过业务流程的情况.
-------------最后我们模拟一下用委托来实现类似于事件的方法----------------------------
public class OptimizeBugManager { /// <summary> /// 发送委托 /// </summary> public delegate void SendDelegate(string name); /// <summary> /// 发送委托 /// </summary> private SendDelegate _OptimizeSendEvent; /// <summary> /// 添加订阅者 /// </summary> /// <param name="sendDelegate"></param> public void AddSendDelegate(SendDelegate sendDelegate) { if (sendDelegate != null) { if (_OptimizeSendEvent != null) { _OptimizeSendEvent += sendDelegate; } else { _OptimizeSendEvent = sendDelegate; } } } /// <summary> /// 移除订阅者 /// </summary> /// <param name="sendDelegate"></param> public void RemoveSendDelegate(SendDelegate sendDelegate) { if (sendDelegate != null) { if (_OptimizeSendEvent != null) { _OptimizeSendEvent -= sendDelegate; } } } /// <summary> /// 监听bug的方法 /// </summary> public void OldBugMonitor(string name) { //监听bug开始 //此处省略代码1万行 //发生bug if (_OptimizeSendEvent != null) { _OptimizeSendEvent(name); } //发送完毕的业务代码开始 //此处省略代码1千行 //发送完毕的业务代码结束 } }
这种情况下因为委托是一个私有的,所以外面除了通过添加和删除这两个方法无法直接对委托进行操作,所以,这样也不会被重写也不会跳过应有的业务逻辑来执行事件
而观察者模式,其实就是说在触发一个动作的时候可以去追加一些业务,而不影响原有代码编写的一种情况,上述的代码其实就是观察者模式的一种写法,如果我们用普通的写法的时候
我们可能就要再添加一个逻辑的时候不断地去修改我们的业务代码,造成麻烦.
下面我来编写一段关于不用观察者模式的情况.
public class BugManager { /// <summary> /// 发生bug的方法 /// </summary> /// <returns></returns> public void BugMonitor() { //监听bug开始 //此处省略代码1万行 //发生bug SendEmail();//发送邮件 SendSms();//发送短信
//!!!!------如果不用观察者模式我们就需要不断地在这个地方去添加方法的调用,我们的代码就不好分离,在修改的时候也会容易出错,不适合团队的业务开发 //发送完毕的业务代码开始 //此处省略代码1千行 //发送完毕的业务代码结束 } /// <summary> /// 发邮件 /// </summary> public void SendEmail() { Console.WriteLine("发送邮件给A!"); Console.WriteLine("发送邮件给B!"); } /// <summary> /// 发短信 /// </summary> public void SendSms() { Console.WriteLine("发送短信给A!"); Console.WriteLine("发送短信给B!"); } }
案例地址:链接:https://pan.baidu.com/s/1dFk29KP 密码:90o8