【設計模式】 觀察者模式
1、定義
1.1 標準定義
觀察者模式(Observer Pattern)也叫做發布訂閱模式(Publish/subscribe),它是一個在項目中經常使用的模式,其定義如下:
Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.(定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。)
1.2 通用類圖
我們先來解釋一下觀察者模式的幾個角色名稱:
● Subject被觀察者
定義被觀察者必須實現的職責,它必須能夠動態地增加、取消觀察者。它一般是抽象類或者是實現類,僅僅完成作為被觀察者必須實現的職責:管理觀察者並通知觀察者。
● Observer觀察者
觀察者接收到消息後,即進行update(更新方法)操作,對接收到的信息進行處理。
● ConcreteSubject具體的被觀察者
定義被觀察者自己的業務邏輯,同時定義對哪些事件進行通知。
● ConcreteObserver具體的觀察者
每個觀察在接收到消息後的處理反應是不同,各個觀察者有自己的處理邏輯。
2、實現
2.1 類圖
Subject類,可翻譯為主題或抽象通知者,一般用一個抽象類或者一個借口實現。它把所有對觀察者對象的引用保存在一個聚集裏,每個主題都可以有任何數量的觀察者。抽象主題提供一個借口,可以增加和刪除觀察者對象。
Observer類,抽象觀察者,為所有的具體觀察者定義一個借口,在得到主題的通知時更新自己。這個借口叫做更新接口。抽象觀察者一般用一個抽象類或者一個接口實現。更新接口通常包含一個Update()方法。
ConcreteSubject類,叫做具體主題或具體通知者,將有關狀態存入具體通知者對象;在具體主題的內部狀態改變時,給所有等級過的觀察者發出通知。通常用一個具體子類實現。
ConcreteObserver類,具體觀察者,實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。具體觀察者角色可以保存一個指向一個具體主題對象的引用。
2.2 代碼
2.2.1 觀察者
// Observer.h #ifndef _OBSERVER_H_ #define _OBSERVER_H_ #include <string> #include <list> using namespace std; class Subject; class Observer { public: ~Observer(); virtual void Update(Subject*)=0; protected: Observer(); private: }; class ConcreteObserverA : publicObserver { public: ConcreteObserverA(); ~ConcreteObserverA(); virtual void Update(Subject*); protected: private: string m_state; }; class ConcreteObserverB : public Observer { public: ConcreteObserverB(); ~ConcreteObserverB(); virtual void Update(Subject*); protected: private: string m_state; }; class Subject { public: ~Subject(); virtual void Notify(); virtual void Attach(Observer*); virtual void Detach(Observer*); virtual string GetState(); virtual void SetState(string state); protected: Subject(); private: string m_state; list<Observer*> m_lst; }; class ConcreteSubjectA : public Subject { public: ConcreteSubjectA(); ~ConcreteSubjectA(); protected: private: }; class ConcreteSubjectB : public Subject { public: ConcreteSubjectB(); ~ConcreteSubjectB(); protected: private: }; #endif
// Observer.cpp #include "Observer.h" #include <iostream> #include <algorithm> using namespace std; Observer::Observer(){} Observer::~Observer(){} ConcreteObserverA::ConcreteObserverA(){} ConcreteObserverA::~ConcreteObserverA(){} void ConcreteObserverA::Update(Subject* pSubject) { this->m_state = pSubject->GetState(); cout << "The ConcreteObserverA is " << m_state << std::endl; } ConcreteObserverB::ConcreteObserverB(){} ConcreteObserverB::~ConcreteObserverB(){} void ConcreteObserverB::Update(Subject* pSubject) { this->m_state = pSubject->GetState(); cout << "The ConcreteObserverB is " << m_state << std::endl; } Subject::Subject(){} Subject::~Subject(){} void Subject::Attach(Observer* pObserver) { this->m_lst.push_back(pObserver); cout << "Attach an Observer\n"; } void Subject::Detach(Observer* pObserver) { list<Observer*>::iterator iter; iter = find(m_lst.begin(),m_lst.end(),pObserver); if(iter != m_lst.end()) { m_lst.erase(iter); } cout << "Detach an Observer\n"; } void Subject::Notify() { list<Observer*>::iterator iter = this->m_lst.begin(); for(;iter != m_lst.end();iter++) { (*iter)->Update(this); } } string Subject::GetState() { return this->m_state; } void Subject::SetState(string state) { this->m_state = state; } ConcreteSubjectA::ConcreteSubjectA(){} ConcreteSubjectA::~ConcreteSubjectA(){} ConcreteSubjectB::ConcreteSubjectB(){} ConcreteSubjectB::~ConcreteSubjectB(){}
2.2.2 調用
// main.cpp #include "Observer.h" #include <iostream> using namespace std; int main() { Observer* p1 = new ConcreteObserverA(); Observer* p2 = new ConcreteObserverB(); Observer* p3 = new ConcreteObserverA(); Subject* pSubject = new ConcreteSubjectA(); pSubject->Attach(p1); pSubject->Attach(p2); pSubject->Attach(p3); pSubject->SetState("old"); pSubject->Notify(); cout << "-------------------------------------" << endl; pSubject->SetState("new"); pSubject->Detach(p3); pSubject->Notify(); return 0; }
2.2.3 執行結果
3、總結
3.1 優點
● 觀察者和被觀察者之間是抽象耦合
如此設計, 則不管是增加觀察者還是被觀察者都非常容易擴展。
● 建立一套觸發機制
根據單一職責原則,每個類的職責是單一的,那麽怎麽把各個單一的職責串聯成真實世界的復雜的邏輯關系呢? 比如, 我們去打獵, 打死了一只母鹿, 母鹿有三個幼崽,因失去了母鹿而餓死,屍體又被兩只禿鷹爭搶,因分配不均,禿鷹開始鬥毆,然後羸弱的禿鷹死掉,生存下來的禿鷹,則因此擴大了地盤……這就是一個觸發機制,形成了一個觸發鏈。觀察者模式可以完美地實現這裏的鏈條形式。
3.2 缺點
觀察者模式需要考慮一下開發效率和運行效率問題, 一個被觀察者,多個觀察者,開發和調試就會比較復雜,如果通知默認是順序執行, 一個觀察者卡殼,會影響整體的執行效率。 在這種情況下, 一般考慮采用異步的方式。
多級觸發時的效率更是讓人擔憂,大家在設計時註意考慮。
3.3 使用場景
● 關聯行為場景,當一個對象的改變需要同時改變其他對象的時候,而且它不知道具體有多少對象有待改變時。 需要註意的是, 關聯行為是可拆分的, 而不是“組合”關系。
● 事件多級觸發場景。
● 跨系統的消息交換場景, 如消息隊列的處理機制。
3.4 註意事項
● 廣播鏈的問題
如果你做過數據庫的觸發器,你就應該知道有一個觸發器鏈的問題,比如表A上寫了一個觸發器,內容是一個字段更新後更新表B的一條數據,而表B上也有個觸發器,要更新表C,表C也有觸發器……完蛋了,這個數據庫基本上就毀掉了!我們的觀察者模式也是一樣的問題,一個觀察者可以有雙重身份,既是觀察者,也是被觀察者, 這沒什麽問題呀,但是鏈一旦建立, 這個邏輯就比較復雜, 可維護性非常差, 根據經驗建議,在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者, 也就是說消息最多轉發一次( 傳遞兩次) , 這還是比較好控制的。
註意:它和責任鏈模式的最大區別就是觀察者廣播鏈在傳播的過程中消息是隨時更改的, 它是由相鄰的兩個節點協商的消息結構; 而責任鏈模式在消息傳遞過程中基本上保持消息不可變,如果要改變,也只是在原有的消息上進行修正。
● 異步處理問題
這個EJB是一個非常好的例子,被觀察者發生動作了,觀察者要做出回應,如果觀察者比較多,而且處理時間比較長怎麽辦?那就用異步唄,異步處理就要考慮線程安全和隊列的問題,這個大家有時間看看Message Queue,就會有更深的了解。
【設計模式】 觀察者模式