[C++設計模式]observer 觀察者模式

有這麼一種鬆耦合的需求:

有一些類的對象對類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;
/////////////////////抽象模式定義
class CObservable;
//觀察者,純虛基類
classCObserver
{
public:
CObserver::CObserver(){};
virtualCObserver::~CObserver(){};
//當被觀察的目標發生變化時,通知調用該方法
//來自被觀察者pObs,擴展參數爲pArg
virtualvoidUpdate(CObservable*pObs,void*pArg=NULL)=0;
};
//被觀察者,即Subject
classCObservable
{
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;
};
/////////////////Main
intmain()
{
//目標(被觀察者)
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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章