zoukankan      html  css  js  c++  java
  • 设计模式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,进行更新。

  • 相关阅读:
    Get distinct count of rows in the DataSet
    单引号双引号的html转义符
    PETS Public English Test System
    Code 39 basics (39条形码原理)
    Index was outside the bounds of the array ,LocalReport.Render
    Thread was being aborted Errors
    Reportviewer Error: ASP.NET session has expired
    ReportDataSource 值不在预期的范围内
    .NET/FCL 2.0在Serialization方面的增强
    Perl像C一样强大,像awk、sed等脚本描述语言一样方便。
  • 原文地址:https://www.cnblogs.com/any91/p/3247974.html
Copyright © 2011-2022 走看看