觀察者模式:
觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
- 抽象主題(Subject)角色:主題角色把所有對觀察考對象的引用儲存在一個聚集裡,每個主題都可以有任何數量的觀察者。抽象主題提供一個介面,可以增加和刪除觀察者對象,主題角色又叫做抽象被觀察者(Observable)角色,一般用一個抽象類別或者一個介面實現。
- 抽象觀察者(Observer)角色:為所有的具體觀察者定義一個介面,在得到主題的通知時更新自己。這個介面叫做更新介面。抽象觀察者角色一般用一個抽象類別或者一個介面實現。在這個示意性的實現中,更新介面只包含一個方法(即Update()方法),這個方法叫做更新方法。
- 具體主題(ConcreteSubject)角色:將有關狀態存入具體現察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者角色(Concrete Observable)。具體主題角色通常用一個具體子類實現。
- 具體觀察者(ConcreteObserver)角色:儲存與主題的狀態自恰的狀態。具體現察者角色實現抽象觀察者角色所要求的更新介面,以便使本身的狀態與主題的狀態相協調。如果需要,具體現察者角色可以儲存一個指向具體主題對象的引用。具體觀察者角色通常用一個具體子類實現。
從具體主題角色指向抽象觀察者角色的合成關係,代表具體主題對象可以有任意多個對抽象觀察者對象的引用。之所以使用抽象觀察者而不是具體觀察者,意味著主題對象不需要知道引用了哪些ConcreteObserver類型,而只知道抽象Observer類型。這就使得具體主題對象可以動態地維護一系列的對觀察者對象的引用,並在需要的時候調用每一個觀察者共有的Update()方法。這種做法叫做"針對抽象編程"。(參考呂震宇老師的部落格)
Observer Pattern發展到現在已經演變成好好幾個版本,最為經典的還是老鼠出洞的例子,此時,貓開始跑起去抓老鼠,這是單任務,而加上娃娃開始哭,這就成了觀察者的多任務,用到自己工作中去,在公司的內部管理系統中,當其中一個部門將一個客戶刪除之後,同時把這個訊息通知到其他部門的每個工作人員,這個時候我們便可以使用觀察者模式的多任務模式。
當單任務的時候我們可以通過對象的引用,直接調用此任務方法,但為多任務的時候,我們可以選擇使用List<對象>,在被觀察者行動的時候,由被觀察者自身遍曆所有的對象,進行通知,簡單的,我們可以使用delegate註冊對象,最後通過觸發事件通知到每一個觀察者。
首先來看代碼:
View Code
using System;using System.Collections.Generic;using System.Text;namespace ObserverPattern{ //定義刪單抽象介面 public interface Observable { void Delete(int id); } //合作部類,可以刪除單子,這個時候通知到註冊到代理事件中的每一個對象,調用更新方法 class CooperationObservable:Observable { public delegate void iDelegate(int id); public event iDelegate aDeletage; public void Delete(int id) { aDeletage(id); } } //觀察者介面 public interface Observer { void Update(int id); } //觀察者對象 class ConcreateObserver : Observer { string name; public string Name { set { name = value; } get { return name; } } public ConcreateObserver(string aname) { name = aname; } //進行更新 public void Update(int id) { Console.WriteLine(Name+":"+id+"單子被刪除"); } } class Program { static void Main(string[] args) { //三個觀察者 ConcreateObserver aObserver = new ConcreateObserver("王瑞"); ConcreateObserver bObserver = new ConcreateObserver("王玲玲"); ConcreateObserver cObserver = new ConcreateObserver("魏晴"); CooperationObservable aSubject = new CooperationObservable(); aSubject.aDeletage += new CooperationObservable.iDelegate(aObserver.Update); aSubject.aDeletage += new CooperationObservable.iDelegate(bObserver.Update); aSubject.aDeletage += new CooperationObservable.iDelegate(cObserver.Update); aSubject.Delete(15); } }}
當然,如果我們非要使用delegate實現的話,這段代碼還有很大的最佳化空間。因為delegate本身就實現了抽象,它就是針對介面進行編程,我們完全可以在我們需要的時候註冊到delegate中我們需要的方法。
來看不適用delegate的情況:
View Code
using System;using System.Collections.Generic;using System.Text;namespace ObserverPattern{ //定義刪單抽象介面 public interface Observable { void Delete(int id); } //合作部類,可以刪除單子,實現註冊方法,將觀察者註冊到觀察者列表當中 class CooperationObservable:Observable { List<Observer> listObserver = new List<Observer>(); public void Delete(int id) { foreach(Observer i in listObserver) i.Update(id); } public void Register(Observer aObserver) { listObserver.Add(aObserver); } } //觀察者介面 public interface Observer { void Update(int id); } //觀察者對象 class ConcreateObserver : Observer { string name; public string Name { set { name = value; } get { return name; } } public ConcreateObserver(string aname) { name = aname; } //進行更新 public void Update(int id) { Console.WriteLine(Name+":"+id+"單子被刪除"); } } class Program { static void Main(string[] args) { //三個觀察者 ConcreateObserver aObserver = new ConcreateObserver("王瑞"); ConcreateObserver bObserver = new ConcreateObserver("王玲玲"); ConcreateObserver cObserver = new ConcreateObserver("魏晴"); CooperationObservable aSubject = new CooperationObservable(); aSubject.Register(aObserver); aSubject.Register(bObserver); aSubject.Register(cObserver); aSubject.Delete(15); } }}
Observer Pattern有好多實現方式,比如上面的代碼,我們可以在觀察者中實現註冊方法,效果是一樣的。
觀察者模式的優缺點
Observer模式的優點是實現了展示層和資料邏輯層的分離,並定義了穩定的更新訊息傳遞機制,類別清晰,並抽象了更新介面,使得可以有各種各樣不同的展示層(觀察者)。
但是其缺點是每個外觀對象必須繼承這個抽像出來的介面類,這樣就造成了一些不方便,比如有一個別人寫的外觀對象,並沒有繼承該抽象類別,或者介面不對,我們又希望不修改該類直接使用它。雖然可以再應用Adapter模式來一定程度上解決這個問題,但是會造成更加複雜煩瑣的設計,增加出錯幾率。
觀察者模式的效果有以下幾個優點:
(1)觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體現察者聚集,每一個具體現察者都符合一個抽象觀察者的介面。被觀察者並不認識任何一個具體觀察者,它只知道它們都有一個共同的介面。由於被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬於不同的抽象化層次。
(2)觀察者模式支援廣播通訊。被觀察者會向所有的登記過的觀察者發出通知。
觀察者模式有下面的一些缺點:
(1)如果一個被觀察者對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
(2)如果在被觀察者之間有循環相依性的話,被觀察者會觸發它們之間進行迴圈調用,導致系統崩潰。在使用觀察考模式時要特別注意這一點。
(3)如果對觀察者的通知是通過另外的線程進行非同步投遞的話,系統必須保證投遞是以自恰的方式進行的。
(4)雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎麼發生變化的。
轉載請註明出處:Edward_jie,http://www.cnblogs.com/promise-7