• 设计模式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,进行更新。

  • 相关阅读:
    多进程 与并发
    socket之 udp用法 dns 多道 进程
    粘包
    socket tcp
    数据集特点
    secureCRT
    算法
    auto-encoder小记
    pytorch dataloader num_workers
    CNN试验记录
  • 原文地址:https://www.cnblogs.com/any91/p/3247974.html
Copyright © 2020-2023  润新知