一. 概述

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

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

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

设计模式C++描述----04.观察者(Observer)模式的更多相关文章

  1. 《Head First 设计模式》ch.2 观察者(Observer)模式

    观察者模式 定义了对象之间一对多以来,这样一来,当一个对象改变状态时,它所有的依赖者都会收到通知并自动更新 设计原则-松耦合 松耦合将对象之间的互相依赖降到了最低——只要他们之间的接口仍被遵守 观察者 ...

  2. Java 实现观察者(Observer)模式

    1. Java自带的实现 类图 /** * 观察目标 继承自 java.util.Observable * @author stone * */ public class UpdateObservab ...

  3. Java设计模式之从[星际争霸的兵种升级]分析观察者(Observer)模式

    观察者模式定义对象的一种一对多的依赖关系.当一个对象的状态发生改变时.全部依赖于它的对象都会得到通知并被自己主动更新. 一个简单的样例是.在星际争霸的虫族中有一个0基础单位叫做跳狗(Zergling) ...

  4. 观察者(Observer)模式

    观察者模式又叫做发布-订阅模式(Publish.Subscribe)模式.模型-视图模式(Model/View)模式.源-监听器模式(Source/Listener)模式或从属者(Dependents ...

  5. 《图解设计模式》读书笔记8-1 Observer模式

    目录 示例程序 程序类图 程序 角色和类图 角色 类图 思路拓展 可复用性 Observer的顺序 MVC模式 Observer模式 Observer模式即观察者模式,该模式中,被观察者的状态发生变化 ...

  6. 面向对象设计模式——观察者(OBSERVER)模式

    定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.  Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...

  7. 设计模式之观察者(OBSERVER)模式

    定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.  Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...

  8. Head First 设计模式 —— 02. 观察者 (Observer) 模式

    思考题 在我们的一个实现中,下列哪种说法正确?(多选) P42 public class WeatherDate { // 实例变量声明 public void measurementsChanged ...

  9. java观察者(Observer)模式

    观察者模式:     试想,在电子商务网站上,一个用户看中了一件一份,但是当时衣服的价格太贵,你需要将衣服收藏,以便等衣服降价时自动通知该用户.这里就是典型的观察模式的例子.     1.观察者模式的 ...

随机推荐

  1. Hadoop源代码点滴-基础概念

    大数据特征:volume(数量).variety(多样性).velocity(产生的速度) 大数据特征:多.乱.快.杂 数据的来源:业务数据.日志.管理文档(OCR).互联网.物联网.外购

  2. Hibernate 中setResultTransformer使用

    在使用hibernate框架,查询数据库多张表或者单张表的某几个需要的字段数据时,往往只能通过sql语句配合setResultTransformer将查询到的数据封装到一个map集合中,再将map集合 ...

  3. PyCharm中创建项目时,在所创建的python虚拟环境下的pip失效

    在这篇博文里,我简单地叙述了我在使用PyCharm创建一个flask项目时遇到的问题,以及我解决这个问题的过程.其中比较值得注意的点有:①PyCharm创建新项目时的解释器配置②Python虚拟环境的 ...

  4. 转载:Docker入门只需看这一篇就够了

    最近项目中需要用到 Docker 打包,于是上网查找资料学习了 Docker 的基本命令,记录一下自己遇到的一些错误. 准备开始自己写,结果看到了阮一峰老师的文章,瞬间就没有写下去的动力了,转载大佬的 ...

  5. Angular 样式绑定

    1. style.propertyName [style.Css属性名] = 'Css属性值变量'/"'css属性值'" // app.component.ts export cl ...

  6. Yii2.0怎么设置时区?

    在config文件夹下,main.php 中, return [ 'charset' => 'utf-8', 'language' => 'zh-CN', 'timeZone' => ...

  7. 【集群监控】Docker上部署Prometheus+Alertmanager+Grafana实现集群监控

    Docker部署 下载 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.re ...

  8. js构造函数的浅薄理解

    任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数 如:任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数 : fuction Preson(){...} var pres ...

  9. css浮动产生和清除浮动的几种方式

    浮动的语法:float:left/right; 浮动float的原本设计了作用初衷是为了实现文字环绕效果 浮动产生负作用: 1.背景不能显示 2.边框不能撑开父元素 3.margin padding设 ...

  10. zoj 3886 Nico Number

    中文题面: 问题描述] 我们定义一个非负整数是“好数”,当且仅当它符合以下条件之一: 1. 这个数是0或1 2. 所有小于这个数且与它互质的正整数可以排成一个等差数列 例如,8就是一个好数,因为1,3 ...