【原】从头学习设计模式(六)——观察者模式

一、引入

  在系统设计中,我们常常需要设计一种消息通知的模块,从服务器端将消息分发给客户端。这样的功能实现可以有很多不同的方式,今天我们来主要介绍一下设计模式中针对这种情况的一种处理方法,也就是观察者模式。观察者模式又常被叫作发布-订阅模式,如果你曾经向移动公司订过手机报,那就很容易理解了,就是把你的手机号注册到移动的手机报发送大名单里,系统就会定时给你发消息了。观察者模式的实现有很多种不同形式,比较直观的就是今天我们本篇要介绍的 “注册——通知”形式。首先按老规矩,我们先来看一下标准定义。

  观察者模式(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. 
 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. 
 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. 
 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.观察者模式支持广播通信。

缺点:

如果有很多观察者同时监听一个主题角色对象,主题角色要花费很多时间去通知每个观察者。可以考虑用多线程的方式去投递通知。

原文地址:https://www.cnblogs.com/ytaozhao/p/3406408.html