觀察者模式

概念

觀察者模式又被稱為發布-訂閱模式、模型-視圖模式、源-收聽者模式或從屬者模式。

在此種模式中,一個目標物件管理所有相依于它的觀察者物件,并且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統

實現

實現觀察者模式有很多形式,比較直觀的一種是使用一種注冊——通知——撤銷注冊的形式。下面的三個圖詳細的描述了這樣一種過程:

3801213fb80e7bec49b2574d2e2eb9389b506b35.jpg
  • 觀察者
    (Observer)將自己注冊到被觀察對象(Subject)中,被觀察對象將觀察者存放在一個容器(Container)里。
    被觀察者不應該過問觀察者的具體類型,而是應該使用觀察者的接口。這樣的優點是:假定程序中還有別的觀察者,那么只要這個觀察者也是相同的接口實現即可。一個被觀察者可以對應多個觀察者,當被觀察者發生變化的時候,他可以將消息一一通知給所有的觀察者。基于接口,而不是具體的實現

  • 被觀察
    被觀察對象發生了某種變化(如圖中的SomeChange),從容器中得到所有注冊過的觀察者,將變化通知觀察者。

  • 撤銷觀察
    觀察者告訴被觀察者要撤銷觀察,被觀察者從容器中將觀察者去除。

  • 代碼

// 觀察者,純虛基類
class CObserver
{
public:
    CObserver(){  };
    virtual ~CObserver(){   };
    //當被觀察者的目標發生變化時,通知調用該方法
    virtual void Update(CObservable* pObs, void* pArg = NULL) = 0;
};

//被觀察者 Subject
class CObservable
{
public:
    CObservable() : m_bChanged(false) {  };
    virtual ~CObservable() {  };
    //注冊觀察者
    void Attach(CObserver* pObs) {
        if (!pObs)   return;
        m_setObs.insert(pObs);
    }
    //注銷觀察者
    void Detach(CObserver* pObs) {  
        if (!pObs)   return;
        m_setObs.erase(pObs);
    }
    //通知所有觀察者
    void Notify(void* pArg = NULL) {
        if (!HasChanged())   return;
        for (auto itr = m_setObs.begin(); itr != m_setObs.end(); itr++) {
            (*itr)->Update(this, pArg);
        }
        ClearChanged();
    }
    void DetachAll();               //注銷所有觀察者
    bool HasChanged();              //測試目標狀態是否變化
    int GetObserversCount();        //獲取觀察者數量
protected:
    void SetChanged();              //設置狀態變化
    void ClearChanged();            //初始化目標為未變化狀態
private:
    bool m_bChanged;               
    set<CObserver*> m_setObs;      // 保存所有觀察者
};
  • 具體應用類實現 "被觀察者"
//bloger是發布者
class CBloger : public CObservable
{
public:
    void Publish(const string &strContent){
        cout << "bloger publish, content: " << strContent << endl;
        SetChanged();
        Notify(const_cast<char*>(strContent.c_str()));  // 調用接口,通知所有注冊的觀察者
    }
};

//portal是發布者
class CPortal : public CObservable
{
public:
    void Publish(const string &strContent){
        cout << "portal publish, content: " << strContent << endl;
        SetChanged();
        Notify(const_cast<char*>(strContent.c_str()));  // 調用接口,通知所有注冊的觀察者
    }
};
  • 具體應用類實現 "觀察者"
//RSS閱讀器,觀察者
class CRSSReader : public CObserver
{
public:
    CRSSReader(const string &strName) : m_strName(strName){}
    virtual void Update(CObservable* pObs, void* pArg = NULL){
        char* pContent = static_cast<char*>(pArg);
        //觀察多個目標
        if (dynamic_cast<CBloger*>(pObs)) {
            cout << m_strName << " updated from bloger, content: " 
                 << pContent << endl;
        } else if (dynamic_cast<CPortal*>(pObs)){
            cout << m_strName << " updated from portal, content: " 
                 << pContent << endl;
        }
    }
private:
    string m_strName;
};
  • 測試
CBloger* pBloger = new CBloger();
CPortal* pPortal = new CPortal();
CRSSReader* pRssReader = new CRSSReader("rss reader");
pBloger->Attach(pRssReader);  //bloger注冊觀察者
pPortal->Attach(pRssReader);  //portal注冊觀察者
 
pBloger->Publish("博客");   
pPortal->Publish("門戶");
//**************************//輸出
bloger publish, content: 博客
![Uploading 3801213fb80e7bec49b2574d2e2eb9389b506b35_814893.jpg . . .]
notify observers...
rss reader updated from bloger, content: 博客

portal publish, content: 門戶
notify observers...
rss reader updated from portal, content: 門戶
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容

  • 先看下觀察者模式的定義: The Observer Pattern defines a one-to-man...
    Java天天閱讀 285評論 0 0
  • 介紹 觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開。舉個例子,用戶界面可以作為一個觀察者,業務...
    花花是男神閱讀 217評論 0 0
  • 談到KVO我們就會聯想到『觀察者模式』,其實KVO就是在oc中實現觀察者模式。那下面就對觀察者做一個簡單介紹: 觀...
    阿湯8阿義閱讀 2,230評論 1 3
  • 類圖 基本介紹 觀察者模式是一種對象行為模式。它定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變...
    三兒二三閱讀 455評論 0 3
  • 推薦指數: 6.0 書籍主旨關鍵詞:特權、焦點、注意力、語言聯想、情景聯想 觀點: 1.統計學現在叫數據分析,社會...
    Jenaral閱讀 5,740評論 0 5