定義:
定義了對象之間一對多的關係,這樣一來,當一個對象發生變化時,他的所有依賴者都會收到通知,並自動更新.
關鍵詞:
observer : 觀察者
subject : 主題
concreteObserver: 具體觀察者
結構圖:
應用情境:
當一個對象的狀態變化時,需要通知其他多個對象進行同步更新時,可以使用此模式(如果有且只有一個對象需要更新,就沒有必要了)
當需要動態增加刪除需要同步的對象時
對象僅僅需要將自己的更新通知其他對象,而無需知道其他對象實現細節時.
優點:
subject 和 observer是高內聚,低耦合的,他們僅通過介面進行聯絡
無需指定具體的observer
缺點:
松耦合導致代碼之間看起來關係不明顯,比較難理解.
例子:
日誌記錄.當發生一個事件或者有狀態變化時,需要日誌記錄下來.要求同時有資料庫記錄和檔案記錄.該如何寫的?
通常寫法是:
public class Record{ public void notify() { DatabaseLog d = new DatabaseLog(); d.RecordToDataBase(); FileLog f = new FileLog(); f.RecordToFile(); }}public class DatabaseLog{ public void RecordToDataBase() { //TODO }}public class FileLog{ public void RecordToFile() { //TODO }}
當有變化時,頁面上只需要調用
Record r = new Record(); r.notify();
即可.
但是:如果要求新增一種記錄方式,web記錄時,該如何處理呢?
新增一個WebLog類,然後修改Record類.
public class Record{ public void notify() { DatabaseLog d = new DatabaseLog(); d.RecordToDataBase(); FileLog f = new FileLog(); f.RecordToFile(); WebLog w = new WebLog(); w.RecordToWeb(); }}public class DatabaseLog{ public void RecordToDataBase() { //TODO }}public class FileLog{ public void RecordToFile() { //TODO }}public class WebLog{ public void RecordToWeb() { //TODO }}
於是,我們發現:針對實際編程,每當有新增時,我們都需要添加新的記錄類,並且修改Record類.
仔細觀察三種記錄方式,其實都是在對所發生的事情那個做記錄,只是記錄的方式不一樣罷了.
類圖:
代碼:
public interface ISubject{ void RegisterObserver(IObserver observer); void RemoveObserver(IObserver observer); void NotifyObservers();}public class Record : ISubject{ private ArrayList observersList; public Record() { observersList = new ArrayList(); } public void RegisterObserver(IObserver observer) { observersList.Add(observer); } public void RemoveObserver(IObserver observer) { observersList.Remove(observer); } public void NotifyObservers() { foreach (IObserver observer in observersList) { observer.Record(); } } public void SetEvent(int el) { if (el > 10) { NotifyObservers(); } }}public interface IObserver{ void Record();}public class DatabaseLog : IObserver{ private ISubject subject; public DatabaseLog(ISubject subject) { this.subject = subject; subject.RegisterObserver(this); } public void Record() { //TODO }}public class FileLog : IObserver{ private ISubject subject; public FileLog(ISubject subject) { this.subject = subject; subject.RegisterObserver(this); } public void Record() { //TODO }}public class WebLog : IObserver{ private ISubject subject; public WebLog(ISubject subject) { this.subject = subject; subject.RegisterObserver(this); } public void Record() { //TODO }}
頁面調用:
protected void Page_Load(object sender, EventArgs e) { Record r = new Record(); DatabaseLog dbl = new DatabaseLog(r); FileLog fl = new FileLog(r); WebLog wl = new WebLog(r); r.SetEvent(100); }
此時,如果需要增加其他的記錄方式的話,需要添加一個類實現Iobserver,然後還需要修改用戶端頁面.
通過設定檔,我們還可以使用戶端做到易插拔.
<hr/>
以下來自<<深入淺出設計模式>>
涉及到的設計原則:
封裝變化
在觀察者模式中,會改變的是主題(Subject)的狀態,以及觀察者(Observer)的數量和具體類型.
面向介面編程
觀察者和主題都使用介面,觀察者使用主題的介面,主題使用觀察者的介面.做到了松耦合.
優先使用對象組合,而不是繼承
觀察者模式利用組合講許多觀察者組合進主題,對象之間的這種關係不是通過繼承得到的,而是在運行時運用動態組合產生的.