簡介:ide
觀察者模式,也稱爲訂閱-發佈模式,定義對象間的一種一對多的依賴關係,當一個對象的狀態發生變化時,全部依賴他的對象都獲得通知並被自動更新。測試
主要由如下幾個部分組成:this
a.Subject目標對象。它具備如下特徵:spa
一個目標能夠被多個觀察者訂閱code
提供訂閱和取消訂閱的方法server
當目標對象狀態發生變化時,通知全部訂閱者。對象
把Subject獨立出來是由於他提供了觀察者模式中常見的三個特徵,每一個觀察者模式都是這樣,能夠抽象出來。具體的單獨邏輯可放在ConcreteSubject具體的目標實現對象中。接口
b.Observer定義觀察者的接口。提供方法(通常爲接口),當目標對象發生變化通知過來,作對應的響應操做。能夠在該方法中調用Subject目標對象,以獲取目標對象的數據。get
c.ConcreteSubject具體的目標實現對象。能夠維護具體對象的一些個性化屬性,如目標狀態等。string
d.ConcreteObserver觀察者的具體實現對象。提供處理目標對象變化的具體響應操做。
具體實例:
報社發行報紙,廣大讀者能夠訂報,也能夠取消訂報。報社維護訂閱者的名單,一旦有新的報紙印刷出來,及時發佈給廣大讀者。
Subject目標對象:提供觀察者模式的基本功能
上面的例子中,廣大讀者都在觀察同一個報社對象,這個報社對象就是被觀察的目標。通常建議在目標接口名稱後面加上Subject
/// <summary> /// Subject目標對象 /// </summary> public class NewsPaperSubject { /// <summary> /// 維護有效訂閱者的列表。 /// </summary> protected List<ReaderObserver> observerList = new List<ReaderObserver>(); /// <summary> /// 提供公共接口,供註冊訂閱者 /// </summary> /// <param name="aObserver"></param> public void Attach(ReaderObserver aObserver) { observerList.Add(aObserver); } /// <summary> /// 提供公共接口,供取消註冊訂閱者 /// </summary> /// <param name="aObserver"></param> public void Detach(ReaderObserver aObserver) { observerList.Remove(aObserver); } /// <summary> /// 目標對象發生變化時,通知全部訂閱的觀察者 /// </summary> protected void NotifyAllObservers() { foreach (ReaderObserver observer in observerList) { observer.update(this); } } }
上面的例子中,僅實現了報社的基本功能,並無定義報社出版報紙等功能,是爲了這個更通用。具體的邏輯放在ConcreteSubject中。
ConcreteSubject具體的目標實現對象:真正的模板對象,日報
日報,在原來基類上新增屬性:報紙的出版日期。修改了出版日期,至關於從新出版了新報紙,此時須要通知全部讀者。
/// <summary> /// ConcreteSubject具體的目標實現對象 -日報/// </summary> public class DaliyNewsPaper : NewsPaperSubject { public string Date { get { return _date; } set { _date = value; //改了出版日期,至關於從新出版了新報紙,此時須要通知全部讀者。 NotifyAllObservers(); } } private string _date; }
Observer定義觀察者的接口:觀察者的接口或者抽象類。
通常建議在觀察者接口後面跟Observer
接口更新方法,建議命名以Update。
觀察者訂閱好報紙之後,在家裏等着收報紙便可,沒有其餘的功能須要實現。這裏先抽象出觀察者接口,而後再後續類中具體實現。
/// <summary> /// Observer定義觀察者的接口 /// </summary> public abstract class ReaderObserver { /// <summary> /// 目標對象發生變化通知過來,響應操做接口方法。 /// <param name="aSubject">推送過來的目標對象</param> /// </summary> public abstract void update(NewsPaperSubject aSubject); }
ConcreteSubject具體的目標實現對象:真正的觀察者,讀者
/// <summary> /// ConcreteObserver觀察者的具體實現對象 -讀者 /// </summary> public class DailyNewsPaperReader : ReaderObserver { public string Name { get { return _name; } set { _name = value; } } private string _name; public override void update(NewsPaperSubject aSubject) { Console.WriteLine("{0}已經收到{1}的報紙", _name, (aSubject as DaliyNewsPaper).Date); } }
測試代碼以下:
public class ObserverTest { public static void Main(string[] args) { //建立目標對象:報紙 DaliyNewsPaper newsPaper = new DaliyNewsPaper(); //建立觀察者對象:張三 DailyNewsPaperReader observer1 = new DailyNewsPaperReader(); observer1.Name = "張三"; //張三訂報紙 newsPaper.Attach(observer1); //建立觀察者對象:李四 DailyNewsPaperReader observer2 = new DailyNewsPaperReader(); observer2.Name = "李四"; //李四訂報紙 newsPaper.Attach(observer2); //2013-10-24報紙出版,張3、李四收到報紙 Console.WriteLine("2013-10-24報紙出版了!!!"); newsPaper.Date = "2013-10-24"; Console.WriteLine(); //李四取消訂報 newsPaper.Detach(observer2); //2013-10-25報紙出版,張三收到報紙 Console.WriteLine("2013-10-25報紙出版了!!!"); newsPaper.Date = "2013-10-25"; Console.ReadLine(); } }
補充分析:
1.觀察者模式通常都是目標對象跟觀察者一對多的關係,即一個目標有多個觀察者。
對於觀察者須要觀察多個目標的狀況下,能夠修改觀察者的UPDATE方法,根據傳入參數決定是哪一個目標的變化,或者乾脆定義不一樣名字的UPDATE方法來對應不一樣的目標調用。
2.觀察者模式的變例:區別對待觀察者
好比說:某公司的請假制度爲,請假半天如下的,通知下項目經理和項目組內成員便可,請假3天如下(含3天)的,還須要通知部門經理,請假3天以上的,還須要通知總經理。
實例中,請假對象是一個目標對象,項目經理、項目組成員、部門經理、總經理都是觀察者。可是不一樣的是,並非每次請假都須要通知上述全部人,須要根據請假的天數來決定通知的範圍。
若是使用觀察者模式?怎麼處理?
參考代碼以下:
protected void NotifyObservers() { foreach (ReaderObserver observer in observerList) { //無論請多少天,都須要通知項目經理和項目組成員 observer.getJob().equals("項目經理").Update(this); observer.getJob().equals("項目組成員").Update(this); if (this.Day > 0.5) observer.getJob().equals("部門經理").Update(this); if(this.Death >3) observer.getJob().equals("總經理").Update(this); } }
此時具體目標對象須要重寫抽象目標對象的通知方法,在方法內部根據目標的屬性來決定被通知的觀察者。