設計模式C++描述----04.觀察者(Observer)模式

來源:互聯網
上載者:User

一. 概述

Observer 模式要解決的問題為:建立一個一(Subject)對多(Observer)的依賴關係,並且做到當“一”變化的時候,依賴這個“一”的多也能夠同步改變。

Sbuject 相當於通知者,它提供依賴於它的觀察者Observer 的註冊(Attach)和登出(Detach)操作,並且提供了使得依賴於它的所有觀察者同步的操作(Notify)。

Observer 相當於觀察者,則提供一個Update操作,注意這裡的 Observer 的 Update 操作並不在Observer 改變了Subject目標狀態的時候就對自己進行更新,這個更新操作要延遲到 Subject 對象發出 Notify 通知所有 Observer 進行修改(調用Update)。

二. 舉例

最常見的一個例子就是:對同一組資料進行統計分析時候,我們希望能夠提供多種形式的表示(例如以表格進行統計顯示、柱狀圖統計顯示、百分比統計顯示等)。這些表示都依賴於同一組資料,我們當然需要當資料改變的時候,所有的統計的顯示都能夠同時改變。

結構關係圖如下:

DataSubject : 我們就認為是未經處理資料。

SheetObserver:就認為是表格,用來顯示未經處理資料用的。

ChartObserver :就認為是圖表,也是來顯示未經處理資料的。

代碼如下:

////////////////////////////////////////////////////////////////////////////觀察者基類class Observer{public:virtual ~Observer(){}virtual void Update(Subject* sub) = 0;virtual void PrintInfo() = 0;protected:Observer(){_st = '\0';}string _st;};//////////////////////////////////////////////////////////////////////////  //通知者基類  class Subject{public:virtual ~Subject(){}//註冊觀察者,這樣通知者就能通知到觀察者virtual void Attach(Observer* obv){_obvs->push_front(obv);}//登出觀察者,通知者不再通知觀察者virtual void Detach(Observer* obv){if (obv != NULL)_obvs->remove(obv);}//通知操作,通知後對於每個註冊過的觀察者,將會調用自己的update方法virtual void Notify(){list<Observer*>::iterator it;it = _obvs->begin();for (;it != _obvs->end();it++){(*it)->Update(this);}}virtual void SetState(const string& st) = 0;virtual string GetState() = 0;protected:Subject(){_obvs = new list<Observer*>;}private:list<Observer* >* _obvs;};////////////////////////////////////////////////////////////////////////////具體的資料通知者class DataSubject:public Subject{public:DataSubject(){_st = '\0';}~DataSubject(){}        //自己的狀態string GetState(){return _st;}void SetState(const string& st){_st = st;}private:string _st;};////////////////////////////////////////////////////////////////////////////資料表格觀察者class SheetObserver:public Observer{public:virtual Subject* GetSubject(){return _sub;}        //建構函式裡,把自己註冊到通知者裡SheetObserver(Subject* sub){_sub = sub;_sub->Attach(this);}virtual ~SheetObserver(){_sub->Detach(this);if (_sub != 0)delete _sub;}//更新操作void Update(Subject* sub){_st = sub->GetState(); //具體的資料可以從Subject這個通知者中取PrintInfo();}void PrintInfo(){cout<<"Sheet observer.... "<<_sub->GetState()<<endl;}private:Subject* _sub;};//資料圖表觀察者class ChatObserver:public Observer{public:virtual Subject* GetSubject(){return _sub;}ChatObserver(Subject* sub){_sub = sub;_sub->Attach(this);}virtual ~ChatObserver(){_sub->Detach(this);if (_sub != 0){delete _sub;}}//更新操作 void Update(Subject* sub){_st = sub->GetState();PrintInfo();}void PrintInfo(){cout<<"Chat observer.... "<<_sub->GetState()<<endl;}private:Subject* _sub;};////////////////////////////////////////////////////////////////////////////測試 int main()  {  DataSubject* sub = new DataSubject();//資料通知者Observer* o1 = new SheetObserver(sub);//表格觀察者  Observer* o2 = new ChatObserver(sub);//圖表觀察者  sub->SetState("old data");//資料發生變化sub->Notify();//通知者下發通知 sub->SetState("new data");sub->Notify();o1->Update(sub); //也可以由觀察者自己調用更新函數  return 0;}
說明:
1. 在 Observer 模式的實現中,Subject 維護一個 list 作為儲存其所有觀察者的容器。每當調用 Notify 操作就遍曆 list中的 Observer 對象,並廣播通知改變狀態(調用Observer的Update操作)。
2. 運行樣本程式,可以看到當未經處理資料 Subject 處於狀態 “old” 時候,依賴於它的兩個觀察者都顯示 “old”,當未經處理資料狀態改變為 “new” 的時候,依賴於它的兩個觀察者也都改變為“new”。
3. 可以看到 Observer 與 Subject 互為耦合,但是這種耦合的雙方 都依賴於抽象,而不依賴於具體。

三. MFC中的觀察者模式

MFC 的 View/Document 結構的實現中也採用了觀察者模式。

Document 為模式中的通知者,管理應用程式中的資料,View為模式中的觀察者,以給定的方顯示所關聯的 Document中的資料。CDocument類中定義了一個指標列表,用於儲存對應的 CView 對象,並定義了一個函數用於對鏈表中的所有CView的對象進行更新。

結構如下:


原代碼如下:

//afxwin.hclass CDocument : public CCmdTarget{public:// Operationsvoid AddView(CView* pView);      //註冊操作void RemoveView(CView* pView);   //登出操作// Update Views (simple update - DAG only)      //通知操作void UpdateAllViews(CView* pSender, LPARAM lHint = 0L,CObject* pHint = NULL);protected:CPtrList m_viewList;                // list of views}//DocCore.cppvoid CDocument::AddView(CView* pView){ASSERT_VALID(pView);ASSERT(pView->m_pDocument == NULL); // must not be already attachedASSERT(m_viewList.Find(pView, NULL) == NULL);   // must not be in listm_viewList.AddTail(pView);          //加入鏈表中ASSERT(pView->m_pDocument == NULL); // must be un-attachedpView->m_pDocument = this;OnChangedViewList();    // must be the last thing done to the document}void CDocument::RemoveView(CView* pView){ASSERT_VALID(pView);ASSERT(pView->m_pDocument == this); // must be attached to usm_viewList.RemoveAt(m_viewList.Find(pView));  //從鏈表中刪除pView->m_pDocument = NULL;OnChangedViewList();    // must be the last thing done to the document}void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)// walk through all views{ASSERT(pSender == NULL || !m_viewList.IsEmpty());// must have views if sent by one of themPOSITION pos = GetFirstViewPosition();        //遍曆所有觀察者while (pos != NULL){CView* pView = GetNextView(pos);ASSERT_VALID(pView);if (pView != pSender)pView->OnUpdate(pSender, lHint, pHint);}}
從代碼中我們可以看到,AddView 和 RemoveView 相當於註冊和登出操作,UpdateAllViews 相當於通知操作,通知操作會依次調用各個CView 對象的 OnUpdate,進行更新。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.