觀察者模式

1. 雙向耦合的代碼

class Program
{
    static void Main(string[] args)
    {

        //前臺小姐童子喆
        Secretary tongzizhe = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏關奼", tongzizhe);
        StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);

        //前臺記下了兩位同事
        tongzizhe.Attach(tongshi1);
        tongzizhe.Attach(tongshi2);
        //發現老闆回來
        tongzizhe.SecretaryAction = "老闆回來了!";
        //通知兩個同事
        tongzizhe.Notify();

        Console.Read();
    }
}

//前臺祕書類
class Secretary
{
    //同事列表
    private IList<StockObserver> observers = new List<StockObserver>();
    private string action;

    //增長
    public void Attach(StockObserver observer)
    {
        observers.Add(observer);
    }

    //減小
    public void Detach(StockObserver observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (StockObserver o in observers)
            o.Update();
    }

    //前臺狀態
    public string SecretaryAction
    {
        get { return action; }
        set { action = value; }
    }

}

//看股票的同事
class StockObserver
{
    private string name;
    private Secretary sub;

    public StockObserver(string name, Secretary sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public void Update()
    {
        Console.WriteLine("{0} {1} 關閉股票行情,繼續工做!", sub.SecretaryAction, name);
    }
}

A: 前臺類和這個看股票者類之間怎麼樣?
B: 互相耦合。
A: 若是還有人相關NBA的網上直播,你的前臺類代碼怎麼辦?
B: 首先開放-封閉原則,修改原有代碼就說明書設計不夠好。其次是依賴倒轉原則,咱們應該讓程序都依賴抽象,而不是互相依賴。ide

2. 解耦實踐一

class Program
{
    static void Main(string[] args)
    {
        //前臺小姐童子喆
        Secretary tongzizhe = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏關奼", tongzizhe);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", tongzizhe);

        //前臺記下了兩位同事
        tongzizhe.Attach(tongshi1);
        tongzizhe.Attach(tongshi2);
        //發現老闆回來
        tongzizhe.SecretaryAction = "老闆回來了!";
        //通知兩個同事
        tongzizhe.Notify();


        Console.Read();
    }
}

//前臺祕書類
class Secretary
{
    //同事列表
    private IList<Observer> observers = new List<Observer>();
    private string action;

    //增長
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    //減小
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
            o.Update();
    }

    //前臺狀態
    public string SecretaryAction
    {
        get { return action; }
        set { action = value; }
    }

}

//抽象觀察者
abstract class Observer
{
    protected string name;
    protected Secretary sub;

    public Observer(string name, Secretary sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public abstract void Update();
}


//看股票的同事
class StockObserver : Observer
{
    public StockObserver(string name, Secretary sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 關閉股票行情,繼續工做!", sub.SecretaryAction, name);
    }
}

//看NBA的同事
class NBAObserver : Observer
{
    public NBAObserver(string name, Secretary sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 關閉NBA直播,繼續工做!", sub.SecretaryAction, name);
    }
}

把全部的與具體觀察者耦合的地方都改爲了'抽象觀察者'。
A: 在具體觀察者中,有沒有與具體的類耦合的。
B: 你的意思是'前提祕書'是一個具體的類,也應該抽象出來。
A: 大家的老闆回來,前臺來不及電話了,因而通知你們的任務變成誰來作。
B: 是老闆,其實老闆和前臺都是具體的通知者,這裏觀察者也不該該依賴具體實現,而是一個抽象的通知者。
A: 甲與前臺有矛盾,因而再也不通知甲,她是否應該把這個對象從她加入的觀察者列表中刪除?函數

3. 解耦實踐二

class Program
{
    static void Main(string[] args)
    {
        //老闆胡漢三
        Boss huhansan = new Boss();

        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏關奼", huhansan);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);

        huhansan.Attach(tongshi1);
        huhansan.Attach(tongshi2);

        huhansan.Detach(tongshi1);

        //老闆回來
        huhansan.SubjectState = "我胡漢三回來了!";
        //發出通知
        huhansan.Notify();

        Console.Read();
    }
}

//通知者接口
interface Subject
{
    void Attach(Observer observer);
    void Detach(Observer observer);
    void Notify();
    string SubjectState
    {
        get;
        set;
    }
}

class Secretary : Subject
{
    //同事列表
    private IList<Observer> observers = new List<Observer>();
    private string action;

    //增長
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    //減小
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
            o.Update();
    }

    //前臺狀態
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}

class Boss : Subject
{
    //同事列表
    private IList<Observer> observers = new List<Observer>();
    private string action;

    //增長
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    //減小
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
            o.Update();
    }

    //老闆狀態
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}

//抽象觀察者
abstract class Observer
{
    protected string name;
    protected Subject sub;

    public Observer(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public abstract void Update();
}

//看股票的同事
class StockObserver : Observer
{
    public StockObserver(string name, Subject sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 關閉股票行情,繼續工做!", sub.SubjectState, name);
    }
}

//看NBA的同事
class NBAObserver : Observer
{
    public NBAObserver(string name, Subject sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 關閉NBA直播,繼續工做!", sub.SubjectState, name);
    }
}

代碼結構圖:
圖1工具

4. 觀察者模式

定義一種 一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使它們可以自動更新本身。

圖2

class Program
{
    static void Main(string[] args)
    {
        ConcreteSubject s = new ConcreteSubject();

        s.Attach(new ConcreteObserver(s, "X"));
        s.Attach(new ConcreteObserver(s, "Y"));
        s.Attach(new ConcreteObserver(s, "Z"));

        s.SubjectState = "ABC";
        s.Notify();

        Console.Read();

    }
}

抽象通知者,通常用一個抽象類或者一個接口實現。它把全部的引用保存在一個彙集裏,每一個通知者能夠有任何數量的觀察者。this

abstract class Subject
{
    private IList<Observer> observers = new List<Observer>();

    //增長觀察者
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }
    //移除觀察者
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }
    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
        {
            o.Update();
        }
    }
}

//具體通知者
class ConcreteSubject : Subject
{
    private string subjectState;

    //具體通知者狀態
    public string SubjectState
    {
        get { return subjectState; }
        set { subjectState = value; }
    }
}

抽象觀察者,爲全部具體的觀察者定義一個接口,在獲得主題的通知時,更新本身。spa

abstract class Observer
{
    public abstract void Update();
}

class ConcreteObserver : Observer
{
    private string name;
    private string observerState;
    private ConcreteSubject subject;

    public ConcreteObserver(
        ConcreteSubject subject, string name)
    {
        this.subject = subject;
        this.name = name;
    }
    //更新
    public override void Update()
    {
        observerState = subject.SubjectState;
        Console.WriteLine("觀察者{0}的新狀態是{1}",
            name, observerState);
    }

    public ConcreteSubject Subject
    {
        get { return subject; }
        set { subject = value; }
    }
}

5. 觀察者模式的特色

B: 觀察者模式的動機是什麼?
A: 將一個系統分割成一系列互相協做的類有一個很很差的反作用,那就是須要維護相關對象間的一致性。咱們不但願爲維持一致性而使各種緊密耦合,這樣會給維護,擴展和重用帶來不便。一旦Subject的狀態發生變化,全部的Obsever均可以獲得通知。Subject發出通知時並不知道誰是它的觀察者,也就是說,具體觀察者是誰,它根本不須要知道。而任何一個具體者不知道也不須要知道其餘觀察者的存在。
B: 當一個對象的改變須要同時改變其餘對象的時候。
A: 並且當不知道具體有多少個對象有待改變時,應該考慮用觀察者模式。
B: 當一個對象模型有兩個方面,其中以方面依賴於另以方面,這時用觀察者模式能夠將這二者封裝在獨立的對象中使它們各自獨立的改變和複用。
A: 總的來說,觀察者模式所作的工做其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響另外一邊的變化。
B: 這實在是依賴倒轉原則的最佳體現。
A: 具體的觀察者徹底有多是風馬牛不相及的類,但它們都須要根據通知者的通知來作出update的操做,全部讓它們都實現一個接口。也就是說,抽象觀察者,代碼能夠是抽象的,還能夠是接口。設計

6. 抽象觀察者的不足

A: 儘管當點擊'運行'按鈕,確實是在通知相關的控件發生變化,可是它們是不可能用接口的方式來實現觀察者模式的。由於這些控件都早已被它們的製造商給封裝了。
A: 儘管已經用了抽象觀察者這樣的接口,可是'抽象通知者'仍是依賴'抽象觀察者',也就是說,萬一沒有了抽象觀察者這樣的接口,我這通知的功能就完成不了了。另外就是每一個具體觀察者,它不必定是'更新'的方法調用啊,就像剛纔說的,我但願的是'工具箱'是隱藏,'自動窗口'是打開,這根本不是同名的方法。這應該是不足的地方吧。
B: 是呀,若是通知者和觀察者之間根本就互相不知道,有客戶端來決定通知誰,那就行了。code

7. 事件委託實現

去掉父類觀察者,全部不上一些代碼,並將'更新'方法名改爲各自適合的方法名。server

class Program
{
    static void Main(string[] args)
    {
        //老闆胡漢三
        Boss huhansan = new Boss();

        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏關奼", huhansan);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);

        huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);  //----
        huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);

        //老闆回來
        huhansan.SubjectState = "我胡漢三回來了!";
        //發出通知
        huhansan.Notify();

        Console.Read();


    }
}

通知者接口,抽象通知者不但願依賴抽象觀察者,全部增長和減小的方法也就不必存在了。(抽象觀察者以及不存在了)對象

interface Subject
{
    void Notify();
    string SubjectState
    {
        get;
        set;
    }
}

//事件處理程序的委託
delegate void EventHandler();

class Secretary : Subject
{
    //聲明一事件Update,類型爲委託EventHandler
    public event EventHandler Update;

    private string action;

    public void Notify()
    {
        Update();
    }
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}

class Boss : Subject
{
    //聲明一事件Update,類型爲委託EventHandler
    public event EventHandler Update;

    private string action;

    public void Notify()
    {
        Update();
    }
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}
//看股票的同事
class StockObserver
{
    private string name;
    private Subject sub;
    public StockObserver(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    //關閉股票行情
    public void CloseStockMarket()
    {
        Console.WriteLine("{0} {1} 關閉股票行情,繼續工做!", sub.SubjectState, name);
    }
}

//看NBA的同事
class NBAObserver
{
    private string name;
    private Subject sub;
    public NBAObserver(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    //關閉NBA直播
    public void CloseNBADirectSeeding()
    {
        Console.WriteLine("{0} {1} 關閉NBA直播,繼續工做!", sub.SubjectState, name);
    }
}

8. 事件委託說明

A: 委託就是一種引用的方法類型。一旦爲委託分配了方法,委託將與該方法具備徹底相同的行爲。委託方法的使用能夠像其餘任何方法同樣,具備參數和返回值。委託能夠看做是對函數的抽象,是函數的類,委託的實例將表明一個具體的函數。
A: 一個委託能夠搭載多個方法,全部方法被依次喚醒。更重要的是,它可使得委託對象所搭載的方法並不須要屬於同一個類。
B: 原本在老闆類中的增長和減小的抽象觀察者集合以及通知時遍歷的抽象觀察者都沒必要要了。轉到客服端來讓委託搭載多個方法,這就解決了原本與抽象觀察者的耦合問題。
A: 但委託也是有前提的,那就是委託對象所搭載的全部方法必須既有相同的原形和形式,也就是有相同的參數列表和返回值類型。接口

相關文章
相關標籤/搜索