設計模式-觀察者模式(Observer)

簡介: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);
           }
       }
此時具體目標對象須要重寫抽象目標對象的通知方法,在方法內部根據目標的屬性來決定被通知的觀察者。
相關文章
相關標籤/搜索