【原】从头学习设计模式(六)——观察者模式
一、引入
在系统设计中,我们常常需要设计一种消息通知的模块,从服务器端将消息分发给客户端。这样的功能实现可以有很多不同的方式,今天我们来主要介绍一下设计模式中针对这种情况的一种处理方法,也就是观察者模式。观察者模式又常被叫作发布-订阅模式,如果你曾经向移动公司订过手机报,那就很容易理解了,就是把你的手机号注册到移动的手机报发送大名单里,系统就会定时给你发消息了。观察者模式的实现有很多种不同形式,比较直观的就是今天我们本篇要介绍的 “注册——通知”形式。首先按老规矩,我们先来看一下标准定义。
观察者模式(Observer):定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们可以自动更新自己。
这里有几个关键点:
1.这是一种一对多的关系
2.多个观察者去监听一个对象,观察者是主动的参与的,被监听对象应该不关心有多少个观察者在监听他。
3.被监听对象只管发通知,观察者自己去处理收到通知后的处理动作。
4.观察者可以主动注册或者撤销注册,这样就将观察者和监听对象隔离开来。
二、类图
从以上类图中我们来总结观察者模式中的几个重要要素:
1.抽象主题:抽象的被观察角色,定义了可以增加或删除观察者的方法。一般用一个抽象类或接口实现。(不是必须的)
2.抽象观察者:抽象的观察者,包含一个Update()方法,在得到通知时更新自己。
3.具体主题对象:在具体的主题内部发生变化时,向观察者发出通知。
4.具体观察者对象:在收到主题通知时更新自己状态。
三、示例代码
我们来用一个比较简单的"Server——Client"消息推送的例子来认识一下观察者模式的使用过程。
1.首先需要定义一个抽象的客户端接口,服务器只需要和接口打交道。这样遵守了“面向接口编程,而非面向实现编程”的OO原则
1 /// <summary> 2 /// 客户端抽象接口 3 /// </summary> 4 public interface IClient 5 { 6 void Notification(string message); 7 }
2.具体的客户端实现,每个客户端必须实现用于接收通知并显示通知的 Notification()方法
1 /// <summary> 2 /// 客户端A 3 /// </summary> 4 public class ClientA : IClient 5 { 6 //用于显示接收到消息的方法 7 public void Notification(string message) 8 { 9 Console.WriteLine(string.Format("This is Client A. \n New Message Received:{0} ",message)); 10 } 11 } 12 /// <summary> 13 /// 客户端B 14 /// </summary> 15 public class ClientB:IClient 16 { 17 public void Notification(string message) 18 { 19 Console.WriteLine(string.Format("This is Client B. \n New Message Received:{0}", message)); 20 } 21 } 22 /// <summary> 23 /// 客户端C 24 /// </summary> 25 public class ClientC:IClient 26 { 27 public void Notification(string message) 28 { 29 Console.WriteLine(string.Format("This is Client C. \n New Message Received:{0}", message)); 30 } 31 }
3.服务端的实现,运用了委托和事件来处理与客户端的消息发送
1 /// <summary> 2 /// 服务器 3 /// </summary> 4 public class Server 5 { 6 //负责发送消息的委托和事件 7 //客户端只要将接收消息的方法绑定在这个事件上就可以得到新消息 8 public delegate void SendMessageHandler(string msg); 9 10 public static event SendMessageHandler SendMessageEvent; 11 12 //执行消息发送的方法 13 public static void Notify() 14 { 15 if(SendMessageEvent!=null) 16 { 17 SendMessageEvent(Message); 18 } 19 } 20 21 public static string Message; 22 }
4. 调用示例,服务器向所有客户端发送消息通知。
1 static void Main(string[] args) 2 { 3 //将需要发送通知的客户端集中放到一个列表中管理 4 IList<IClient> ClientList=new List<IClient>(); 5 ClientList.Add(new ClientA()); 6 ClientList.Add(new ClientB()); 7 ClientList.Add(new ClientC()); 8 //将所有客户端的接收消息方法注册到服务器的发送事件上 9 foreach (var client in ClientList) 10 { 11 Server.SendMessageEvent += client.Notification; 12 } 13 //定义服务器要发送的消息内容 14 Server.Message = "You got a message from Server!"; 15 //开始执行发送 16 Server.Notify(); 17 Console.ReadKey(); 18 19 }
5.运行结果
同样,客户端也可以撤销消息的通知事件,我们把调用方法稍作修改,如下
1 static void Main(string[] args) 2 { 3 //创建三个客户端对象 4 ClientA clientA=new ClientA(); 5 ClientB clientB=new ClientB(); 6 ClientC clientC=new ClientC(); 7 8 //分别为每个客户端注册消息事件 9 Server.SendMessageEvent += clientA.Notification; 10 Server.SendMessageEvent += clientB.Notification; 11 Server.SendMessageEvent += clientC.Notification; 12 13 Server.Message = "You got a message from Server!"; 14 Server.Notify(); 15 16 //客户端C取消接收消息通知 17 Console.WriteLine("---------------------- Client C Ignore The Message ---------------------------"); 18 Server.SendMessageEvent -= clientC.Notification; 19 20 Server.Message = "Client C has gone."; 21 Server.Notify(); 22 23 Console.ReadKey(); 24 25 }
运行结果:
取消注册后,Client C 就再也收不到消息通知了。
四、总结
观察者模式实现了抽象与具体的分离,定义了稳定的更新消息传递机制,使类之间各自维护自己的功能,提高系统的可重用性和可维护性。
优点:
1.观察者与被观察者之间建立一个抽象的耦合关系,被观察者(主题角色)并不知道和关心每个一具体的观察者,它只要关心一个共同的观察者接口。
2.观察者模式支持广播通信。
缺点:
如果有很多观察者同时监听一个主题角色对象,主题角色要花费很多时间去通知每个观察者。可以考虑用多线程的方式去投递通知。