定義
觀察者模式(有時又被稱為發布-訂閱Subscribe>模式、模型-視圖View>模式、源-收聽者Listener>模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
基本簡介
(1)抽象主題(Subject)
持有一個觀察者對象的集合,提供增加,刪除觀察者對象的接口,當需要關注的狀態變化時候,需要通知所有持有的觀察者對象。
(2)具體主題(Concrete Subject)
被觀察者的具體實現...
(3)抽象觀察者(Observer)
定義一個接口,在接受通知時候更新狀態
(4)具體觀察者(Concrete Observer)
觀察者的具體實現...
UML圖
示例
(1.1)抽象主題類Subject
/// <summary> /// 抽象主題 /// </summary> public abstract class Subject { /// <summary> /// 所有觀察者對象 /// </summary> private List<Observer> observers = new List<Observer>(); /// <summary> /// 增加觀察者對象 /// </summary> /// <param name="observer"></param> public void AddObserver(Observer observer) { observers.Add(observer); } /// <summary> /// 移除觀察者對象 /// </summary> /// <param name="observer"></param> public void RemoveObserver(Observer observer) { observers.Remove(observer); } /// <summary> /// 發送通知 /// </summary> public void Notify() { foreach (var ob in observers) { ob.Update(); } } }
(1.2)具體主題類ConcreteSubject
/// <summary> /// 具體通知者 /// </summary> public class ConcreteSubject : Subject { /// <summary> /// 具體觀察者狀態 /// </summary> public string SubjectState { get; set; } }
(1.3)抽象觀察者類
/// <summary> /// 觀察者類 /// </summary> public abstract class Observer { public abstract void Update(); }
(1.4)具體觀察者類
/// <summary> /// 具體觀察者 /// </summary> public class ConcreteObserver : Observer { public string observerState { get; set; } public string Name { get; set; } public ConcreteSubject subjcSubject { get; set; } public ConcreteObserver(ConcreteSubject subject, string name) { this.Name = name; this.subjcSubject = subject; } public override void Update() { observerState = subjcSubject.SubjectState; System.Console.WriteLine("the observer's state of {0} is {1}", Name, observerState); } }
(1.5)控制台調用
class Program { static void Main(string[] args) { ConcreteSubject subject = new ConcreteSubject(); subject.AddObserver(new ConcreteObserver(subject, "ObA")); subject.AddObserver(new ConcreteObserver(subject, "ObB")); subject.AddObserver(new ConcreteObserver(subject, "ObC")); subject.SubjectState = "Ready"; subject.Notify(); System.Console.ReadKey(); } }
(1.6)運行結果
優缺點
C#委托改版
我們以一個主題通知發送消息的例子來演示
(1.1)抽象主題
/// <summary> /// 抽象主題 /// </summary> public interface ISubject { void Notify(); }
(1.2)聲明委托和具體主題
/// <summary> /// 聲明委托 /// </summary> public delegate void MsgEvent(); /// <summary> /// 具體主題 /// </summary> public class ConcreteSubject : ISubject { /// <summary> /// 定義委托事件 /// </summary> public event MsgEvent MsgAction; /// <summary> /// 執行通知 /// </summary> public void Notify() { if (MsgAction != null) MsgAction(); } }
(1.3)添加具體觀察者 站內信,郵件,短信
/// <summary> /// 站內信 /// </summary> public class InsideLetterMsg { /// <summary> /// 發送站內信 /// </summary> public void SendInsideLetterMsg() { Console.WriteLine("發送站內信....."); } } /// <summary> /// 郵件 /// </summary> public class MailMsg { /// <summary> /// 發送郵件 /// </summary> public void SendMailMsg() { Console.WriteLine("發送郵件....."); } } /// <summary> /// 短信 /// </summary> public class SMSMsg { /// <summary> /// 發送短信 /// </summary> public void SendSMSMsg() { Console.WriteLine("發送短信....."); } }
(1.4)客戶端調用
class Program { static void Main(string[] args) { ConcreteSubject subject = new ConcreteSubject(); //注冊事件 subject.MsgAction += (new InsideLetterMsg()).SendInsideLetterMsg; //站內信 subject.MsgAction += (new MailMsg()).SendMailMsg; //郵件 subject.MsgAction += (new SMSMsg()).SendSMSMsg; //短信 //開始發送消息了 subject.Notify(); Console.ReadKey(); } }
(1.5)結果
這個例子我們更多關注的是行為,主題對於觀察者行為的執行和通知。我們只需要在調用的時候將觀察者的方法注冊到主題中即可。省去了主題需要維護觀察者對象,循環調用觀察者對象方法的過程。順帶我們也看一下我們創建的委托具體是what?
委托看一看
(1.1)看圖說話
(1.2)System.MulticastDelegate
_invocationList通常這個字段為null,當我們構造一個委托鏈是,他可以引用一個委托數組,也就是說我們給委托+=方法時候,實際是操作它.
我們可以推斷出當我們調用委托方法時候,代碼大致是這樣的(下面代碼不是可運行的代碼,只是預估大概的邏輯,僅供觀看),不知道在觀察者這塊順帶寫了下委托是不是有點
跑偏,委托這里只是順帶提一下,更多的知識面肯定沒涉及到,只是幫助大家理解下.
//從委托鏈中獲取 Delegate[] deleagetset = _invocationList as Delegate[]; if (deleagetset != null) { foreach (MsgAction m in deleagetset) { m(value); //調用每個委托 } } else { //當前不是委托鏈 直接 invoke _methodPtr.Invoke(_target, value); }
上邊的例子只是單純的循環,中途有一個調用委托失敗都沒有健壯的處理,所以MulticastDelegate類提供了另一個實例方法GetInvocationList,用於顯示調用鏈中的每一個委托,具體大家可以查閱相關資料。That's all!
文章代碼鏈接:http://files.cnblogs.com/files/mongo/BlogObserver.zip