设计模式C++描述----04.观察者(Observer)模式

一. 概述

Observer 模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变

Sbuject 相当于通知者,它提供依赖于它的观察者Observer 的注册(Attach)和注销(Detach)操作,并且提供了使得依赖于它的所有观察者同步的操作(Notify)。

Observer 相当于观察者,则提供一个Update操作,注意这里的 Observer 的 Update 操作并不在Observer 改变了Subject目标状态的时候就对自己进行更新,这个更新操作要延迟到 Subject 对象发出 Notify 通知所有 Observer 进行修改(调用Update)

二. 举例

最常见的一个例子就是:对同一组数据进行统计分析时候,我们希望能够提供多种形式的表示(例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。这些表示都依赖于同一组数据,我们当然需要当数据改变的时候,所有的统计的显示都能够同时改变。

结构关系图如下:

DataSubject : 我们就认为是原始数据。

SheetObserver:就认为是表格,用来显示原始数据用的。

ChartObserver :就认为是图表,也是来显示原始数据的。

代码如下:

  1. //////////////////////////////////////////////////////////////////////////  
  2. //观察者基类  
  3. class Observer  
  4. {  
  5. public:  
  6.     virtual ~Observer()  
  7.     {  
  8.     }  
  9.   
  10.     virtual void Update(Subject* sub) = 0;  
  11.     virtual void PrintInfo() = 0;  
  12.       
  13. protected:  
  14.     Observer()  
  15.     {  
  16.         _st = '';  
  17.     }  
  18.       
  19.     string _st;  
  20. };  
  21.   
  22. //////////////////////////////////////////////////////////////////////////    
  23. //通知者基类    
  24. class Subject  
  25. {  
  26. public:  
  27.     virtual ~Subject()  
  28.     {  
  29.     }  
  30.       
  31.     //注册观察者,这样通知者就能通知到观察者  
  32.     virtual void Attach(Observer* obv)  
  33.     {  
  34.         _obvs->push_front(obv);  
  35.     }  
  36.       
  37.     //注销观察者,通知者不再通知观察者  
  38.     virtual void Detach(Observer* obv)  
  39.     {  
  40.         if (obv != NULL)  
  41.             _obvs->remove(obv);  
  42.     }  
  43.       
  44.     //通知操作,通知后对于每个注册过的观察者,将会调用自己的update方法  
  45.     virtual void Notify()  
  46.     {  
  47.         list<Observer*>::iterator it;  
  48.         it = _obvs->begin();  
  49.           
  50.         for (;it != _obvs->end();it++)  
  51.         {  
  52.             (*it)->Update(this);  
  53.         }  
  54.     }  
  55.       
  56.     virtual void SetState(const string& st) = 0;  
  57.     virtual string GetState() = 0;  
  58.       
  59. protected:  
  60.     Subject()  
  61.     {  
  62.         _obvs = new list<Observer*>;  
  63.     }  
  64.       
  65. private:  
  66.     list<Observer* >* _obvs;  
  67. };  
  68.   
  69. //////////////////////////////////////////////////////////////////////////  
  70. //具体的数据通知者  
  71. class DataSubject:public Subject  
  72. {  
  73. public:  
  74.     DataSubject()  
  75.     {  
  76.         _st = '';  
  77.     }  
  78.       
  79.     ~DataSubject()  
  80.     {  
  81.     }  
  82.       
  83.         //自己的状态  
  84.     string GetState()  
  85.     {  
  86.         return _st;  
  87.     }  
  88.       
  89.     void SetState(const string& st)  
  90.     {  
  91.         _st = st;  
  92.     }  
  93.       
  94. private:  
  95.     string _st;  
  96. };  
  97.   
  98. //////////////////////////////////////////////////////////////////////////  
  99. //数据表格观察者  
  100. class SheetObserver:public Observer  
  101. {  
  102. public:  
  103.     virtual Subject* GetSubject()  
  104.     {  
  105.         return _sub;  
  106.     }  
  107.       
  108.         //构造函数里,把自己注册到通知者里  
  109.     SheetObserver(Subject* sub)  
  110.     {  
  111.         _sub = sub;  
  112.         _sub->Attach(this);  
  113.     }  
  114.       
  115.     virtual ~SheetObserver()  
  116.     {  
  117.         _sub->Detach(this);  
  118.         if (_sub != 0)  
  119.             delete _sub;  
  120.     }  
  121.       
  122.     //更新操作  
  123.     void Update(Subject* sub)  
  124.     {  
  125.         _st = sub->GetState(); //具体的数据可以从Subject这个通知者中取  
  126.         PrintInfo();  
  127.     }  
  128.       
  129.     void PrintInfo()  
  130.     {  
  131.         cout<<"Sheet observer.... "<<_sub->GetState()<<endl;  
  132.     }  
  133.   
  134. private:  
  135.     Subject* _sub;  
  136. };  
  137.   
  138. //数据图表观察者  
  139. class ChatObserver:public Observer  
  140. {  
  141. public:  
  142.     virtual Subject* GetSubject()  
  143.     {  
  144.         return _sub;  
  145.     }  
  146.       
  147.     ChatObserver(Subject* sub)  
  148.     {  
  149.         _sub = sub;  
  150.         _sub->Attach(this);  
  151.     }  
  152.       
  153.     virtual ~ChatObserver()  
  154.     {  
  155.         _sub->Detach(this);  
  156.         if (_sub != 0)  
  157.         {  
  158.             delete _sub;  
  159.         }  
  160.     }  
  161.       
  162.     //更新操作   
  163.     void Update(Subject* sub)  
  164.     {  
  165.         _st = sub->GetState();  
  166.         PrintInfo();  
  167.     }  
  168.       
  169.     void PrintInfo()  
  170.     {  
  171.         cout<<"Chat observer.... "<<_sub->GetState()<<endl;  
  172.     }  
  173.   
  174. private:  
  175.     Subject* _sub;  
  176. };  
  177.   
  178.   
  179. //////////////////////////////////////////////////////////////////////////  
  180. //测试   
  181. int main()    
  182. {    
  183.     DataSubject* sub = new DataSubject();//数据通知者  
  184.   
  185.     Observer* o1 = new SheetObserver(sub);//表格观察者    
  186.     Observer* o2 = new ChatObserver(sub);//图表观察者    
  187.   
  188.     sub->SetState("old data");//数据发生变化  
  189.     sub->Notify();//通知者下发通知   
  190.   
  191.     sub->SetState("new data");  
  192.     sub->Notify();  
  193.   
  194.     o1->Update(sub); //也可以由观察者自己调用更新函数    
  195.   
  196.     return 0;  
  197. }  

说明:
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的对象进行更新。

结构如下:


原代码如下:

  1. //afxwin.h  
  2. class CDocument : public CCmdTarget  
  3. {  
  4. public:  
  5.   
  6.     // Operations  
  7.     void AddView(CView* pView);      //注册操作  
  8.     void RemoveView(CView* pView);   //注销操作  
  9.   
  10.   
  11.     // Update Views (simple update - DAG only)      //通知操作  
  12.     void UpdateAllViews(CView* pSender, LPARAM lHint = 0L,  
  13.         CObject* pHint = NULL);  
  14.   
  15. protected:  
  16.   
  17.     CPtrList m_viewList;                // list of views  
  18. }  
  19.   
  20.   
  21. //DocCore.cpp  
  22. void CDocument::AddView(CView* pView)  
  23. {  
  24.     ASSERT_VALID(pView);  
  25.     ASSERT(pView->m_pDocument == NULL); // must not be already attached  
  26.     ASSERT(m_viewList.Find(pView, NULL) == NULL);   // must not be in list  
  27.   
  28.     m_viewList.AddTail(pView);          //加入链表中  
  29.     ASSERT(pView->m_pDocument == NULL); // must be un-attached  
  30.     pView->m_pDocument = this;  
  31.   
  32.     OnChangedViewList();    // must be the last thing done to the document  
  33. }  
  34.   
  35. void CDocument::RemoveView(CView* pView)  
  36. {  
  37.     ASSERT_VALID(pView);  
  38.     ASSERT(pView->m_pDocument == this); // must be attached to us  
  39.   
  40.     m_viewList.RemoveAt(m_viewList.Find(pView));  //从链表中删除  
  41.     pView->m_pDocument = NULL;  
  42.   
  43.     OnChangedViewList();    // must be the last thing done to the document  
  44. }  
  45.   
  46. void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)  
  47.     // walk through all views  
  48. {  
  49.     ASSERT(pSender == NULL || !m_viewList.IsEmpty());  
  50.         // must have views if sent by one of them  
  51.   
  52.     POSITION pos = GetFirstViewPosition();        //遍历所有观察者  
  53.     while (pos != NULL)  
  54.     {  
  55.         CView* pView = GetNextView(pos);  
  56.         ASSERT_VALID(pView);  
  57.         if (pView != pSender)  
  58.             pView->OnUpdate(pSender, lHint, pHint);  
  59.     }  
  60. }  

从代码中我们可以看到,AddView 和 RemoveView 相当于注册和注销操作,UpdateAllViews 相当于通知操作,通知操作会依次调用各个CView 对象的 OnUpdate,进行更新。

原文地址:https://www.cnblogs.com/any91/p/3247974.html