设计模式 - 观察者模式

xiaoxiao2025-03-13  9

来自:https://blog.csdn.net/lwbeyond/article/details/7532107

一. 概述

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.h class CDocument : public CCmdTarget { public:       // Operations     void 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.cpp void CDocument::AddView(CView* pView) {     ASSERT_VALID(pView);     ASSERT(pView->m_pDocument == NULL); // must not be already attached     ASSERT(m_viewList.Find(pView, NULL) == NULL);   // must not be in list       m_viewList.AddTail(pView);          //加入链表中     ASSERT(pView->m_pDocument == NULL); // must be un-attached     pView->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 us       m_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 them       POSITION 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,进行更新 ---------------------  作者:lwbeyond  来源:  原文:https://blog.csdn.net/lwbeyond/article/details/7532107  版权声明:本文为博主原创文章,转载请附上博文链接!

 

转载请注明原文地址: https://www.6miu.com/read-5025750.html

最新回复(0)