【原】從頭學習設計模式(六)——觀察者模式

一、引入
在系統設計中,我們常常需要設計一種消息通知的模塊,從服務器端將消息分發給客戶端。這樣的功能實現可以有很多不同的方式,今天我們來主要介紹一下設計模式中針對這種情況的一種處理方法,也就是觀察者模式。觀察者模式又常被叫作發布-訂閱模式,如果你曾經向移動公司訂過手機報,那就很容易理解了,就是把你的手機號注冊到移動的手機報發送大名單里,系統就會定時給你發消息了。觀察者模式的實現有很多種不同形式,比較直觀的就是今天我們本篇要介紹的 “注冊——通知”形式。首先按老規矩,我們先來看一下標準定義。
觀察者模式(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.觀察者模式支持廣播通信。
缺點:
如果有很多觀察者同時監聽一個主題角色對象,主題角色要花費很多時間去通知每個觀察者。可以考慮用多線程的方式去投遞通知。


浙公網安備 33010602011771號