標籤:
有這麼一種松耦合的需求:
有一些類的對象對類A對象的狀態變化高度興趣,不會改變類A的對象,也不會被類A的對象改變,想以一種較小的代價觀察對類A對象狀態變化。
下面的幾種方式也能實現上述目的
(1)通過類的繼承來共同管理和維護一些感興趣的資料或者狀態,但是耦合度大,不易擴充和維護。
(2)通過調用被觀察者的getter方法擷取資料,這個還是直接的對象調用。
上述兩種方法將使觀察者和被觀察對象之間緊密的耦合起來,從根本上違反物件導向的設計的原則。無論是觀察者“觀察”觀察對象,
還是被觀察者將自己的改變“通知”觀察者,都不應該直接調用。
觀察者模式(有時又被稱為發布-訂閱Subscribe>模式、模型-視圖View>模式、源-收聽者Listener>模式或從屬者模式)中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
實現觀察者模式有很多形式,比較直觀的一種是使用一種“註冊——通知——撤銷註冊”的形式。陳碩的《linux多線程服務端編程 使用muduo c+網路程式庫》中有一個多人聊天的例子,在服務端程式中,要把一個用戶端發來的訊息分發給所有的用戶端,也可以看待是觀察者模式的典型案例,TCP的串連視為註冊,訊息的分發視為通知,TCP串連的斷開視為撤銷註冊。
觀察者(Observer)將自己註冊到被觀察對象(Subject)中,被觀察對象將觀察者存放在一個容器(Container)裡。
被觀察
被觀察對象發生了某種變化(中的SomeChange),從容器中得到所有註冊過的觀察者,將變化通知觀察者。
撤銷觀察
觀察者告訴被觀察者要撤銷觀察,被觀察者從容器中將觀察者去除。
觀察者將自己註冊到被觀察者的容器中時,被觀察者不應該過問觀察者的具體類型,而是應該使用觀察者的介面。這樣的優點是:假定程式中還有別的觀察者,那麼只要這個觀察者也是相同的介面實現即可。一個被觀察者可以對應多個觀察者,當被觀察者發生變化的時候,他可以將訊息一一通知給所有的觀察者。基於介面,而不是具體的實現——這一點為程式提供了更大的靈活性。
C++例子
#include<iostream>#include<set>#include<string>usingnamespacestd;/////////////////////抽象模式定義classCObservable;//觀察者,純虛基類classCObserver{public:CObserver::CObserver(){};virtualCObserver::~CObserver(){};//當被觀察的目標發生變化時,通知調用該方法//來自被觀察者pObs,擴充參數為pArgvirtualvoidUpdate(CObservable*pObs,void*pArg=NULL)=0;};//被觀察者,即SubjectclassCObservable{public:CObservable():m_bChanged(false){};virtual~CObservable(){};//註冊觀察者voidAttach(CObserver*pObs);//登出觀察者voidDetach(CObserver*pObs);//登出所有觀察者voidDetachAll();//若狀態變化,則遍曆觀察者,逐個通知更新voidNotify(void*pArg=NULL);//測試目標狀態是否變化boolHasChanged();//擷取觀察者數量intGetObserversCount();protected://設定狀態變化!!!必須繼承CObservable才能設定目標狀態voidSetChanged();//初始化目標為未變化狀態voidClearChanged();private:boolm_bChanged;//狀態set<CObserver*>m_setObs;//set保證目標唯一性};/////////////////////抽象模式實現voidCObservable::Attach(CObserver*pObs){if(!pObs)return;m_setObs.insert(pObs);}voidCObservable::Detach(CObserver*pObs){if(!pObs)return;m_setObs.erase(pObs);}voidCObservable::DetachAll(){m_setObs.clear();}voidCObservable::SetChanged(){m_bChanged=true;}voidCObservable::ClearChanged(){m_bChanged=false;}boolCObservable::HasChanged(){returnm_bChanged;}intCObservable::GetObserversCount(){returnm_setObs.size();}voidCObservable::Notify(void*pArg/*=NULL*/){if(!HasChanged())return;cout<<"notifyobservers…"<<endl;ClearChanged();set<CObserver*>::iteratoritr=m_setObs.begin();for(;itr!=m_setObs.end();itr++){(*itr)->Update(this,pArg);}}/////////////////////具體應用類定義和實現//bloger是發行者,即被觀察者(subject)classCBloger:publicCObservable{public:voidPublish(conststring&strContent){cout<<"blogerpublish,content:"<<strContent<<endl;SetChanged();Notify(const_cast<char*>(strContent.c_str()));}};//portal是發行者,即被觀察者(subject)classCPortal:publicCObservable{public:voidPublish(conststring&strContent){cout<<"portalpublish,content:"<<strContent<<endl;SetChanged();Notify(const_cast<char*>(strContent.c_str()));}};//RSS閱讀器,觀察者classCRSSReader:publicCObserver{public:CRSSReader(conststring&strName):m_strName(strName){}virtualvoidUpdate(CObservable*pObs,void*pArg=NULL){char*pContent=static_cast<char*>(pArg);//觀察多個目標if(dynamic_cast<CBloger*>(pObs)){cout<<m_strName<<"updatedfrombloger,content:"<<pContent<<endl;}elseif(dynamic_cast<CPortal*>(pObs)){cout<<m_strName<<"updatedfromportal,content:"<<pContent<<endl;}}private:stringm_strName;};//Mail閱讀器,觀察者classCMailReader:publicCObserver{public:CMailReader(conststring&strName):m_strName(strName){}virtualvoidUpdate(CObservable*pObs,void*pArg=NULL){char*pContent=static_cast<char*>(pArg);if(dynamic_cast<CBloger*>(pObs)){cout<<m_strName<<"updatedfrombloger,content:"<<pContent<<endl;}if(dynamic_cast<CPortal*>(pObs)){cout<<m_strName<<"updatedfromportal,content:"<<pContent<<endl;}}private:stringm_strName;};/////////////////Mainintmain(){//目標(被觀察者)CBloger*pBloger=newCBloger();CPortal*pPortal=newCPortal();//觀察者.一個觀察者可以觀察多個目標CRSSReader*pRssReader=newCRSSReader("rssreader");CMailReader*pMailReader=newCMailReader("mailreader");pBloger->Attach(pRssReader);//bloger註冊觀察者pBloger->Attach(pMailReader);//bloger註冊觀察者pPortal->Attach(pRssReader);//portal註冊觀察者pPortal->Attach(pMailReader);//portal註冊觀察者//部落格發布資訊pBloger->Publish("部落格分享設計模式");cout<<endl;//門戶發布資訊pPortal->Publish("門戶分享設計模式");cout<<"\nportaldetachedmailreader"<<endl;pPortal->Detach(pMailReader);cout<<"portalobserverscount:"<<pPortal->GetObserversCount()<<endl<<endl;pPortal->Publish("門戶分享設計模式");system("pause");return0;}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
observer(觀察者模式)