3.3 观察者模式 (Observer)/发布-订阅模式

动机:

在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都能得到通知。如果这样的依赖关系过于紧密,将使得软件不能很好地抵御变化。

使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

代码示例: 文件分割器,添加处理过程进度展示功能。

第一种方法:

分析代码:违背依赖倒置原则

第6行: ProgressBar作为实现细节(表现形式可以多样变化,如图形界面,数字显示等)。其扮演的角色和任务是通知。

将通知的任务不用控件方式(太过细节)实现。

第二种方法:(重构第一种)

添加 IProgress类,作为抽象的通知机制,即观察者(订阅者)。观察者内含有更新方法DoProgress()。具体观察者(MainForm/ConsoleNotifier)继承该类,实现各自更新功能。

FileSplitter作为被观察对象(对象/发布者),其中含有抽象通知机制IProgress指针,利用DoProgress()方法进行通知。

即对象(发布者)不必考虑观察者情况,自动进行通知(发布)工作;

观察者(订阅者)根据自身实际情况选择是否订阅或如何处理通知。

同时考虑多个观察者问题添加List<IProgress*>。

 //FileSplitter1.cpp
class FileSplitter
{
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar; public:
FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
m_filePath(filePath),
m_fileNumber(fileNumber),
m_progressBar(progressBar){ } void split(){ //1.读取大文件 //2.分批次向小文件中写入
for (int i = ; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + ) / progressValue;
m_progressBar->setValue(progressValue);
} } // MainForm1.cpp
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar; public:
void Button1_Click(){ string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str()); FileSplitter splitter(filePath, number, progressBar); splitter.split(); }
};

第二种方法:

 //FileSplitter2.cpp
class IProgress{
public:
virtual void DoProgress(float value)=;
virtual ~IProgress(){}
}; class FileSplitter
{
string m_filePath;
int m_fileNumber; List<IProgress*> m_iprogressList; // 抽象通知机制,支持多个观察者 public:
FileSplitter(const string& filePath, int fileNumber) :
m_filePath(filePath),
m_fileNumber(fileNumber){ } void split(){ //1.读取大文件 //2.分批次向小文件中写入
for (int i = ; i < m_fileNumber; i++){
//... float progressValue = m_fileNumber;
progressValue = (i + ) / progressValue;
onProgress(progressValue);//发送通知
} } void addIProgress(IProgress* iprogress){
m_iprogressList.push_back(iprogress);
} void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
} protected:
virtual void onProgress(float value){ List<IProgress*>::iterator itor=m_iprogressList.begin(); while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value); //更新进度条
itor++;
}
}
}; //MainForm2.cpp
class MainForm : public Form, public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber; ProgressBar* progressBar; public:
void Button1_Click(){ string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str()); ConsoleNotifier cn; FileSplitter splitter(filePath, number); splitter.addIProgress(this); //订阅通知
splitter.addIProgress(&cn); //订阅通知 splitter.split(); splitter.removeIProgress(this); } virtual void DoProgress(float value){
progressBar->setValue(value);
}
}; class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};

定义:

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

类图:

Observer:  IProgress; Update():DoProgress() .

ConcreteSubject: FileSplitter

ConcreteObserver: Mainform / ConsoleNotifier

根据标准的Observer模式定义,也可将addIProgress,removeProgress,onProgress单独定义类(Subject),再将FileSplitter(ConcreteSubject)继承此类。

上述方法2 相当于将类图中的Subject 和 ConcreteSubject合二为一。

要点总结:

1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。

2.目标发送通知时,无需指定观察者,通知会(可以携带通知信息作为参数)自动传播。

onProgress(progressValue);//发送通知,无需考虑具体的观察者问题。

3.观察者自己决定是否订阅通知,目标对象对此一无所知。

79 splitter.addIProgress(this); //订阅通知

80 splitter.addIProgress(&cn); //订阅通知

4.Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。

 

c++ 设计模式5 (Observer / Event 观察者模式)的更多相关文章

  1. 设计模式之 Observer Pattern 观察者模式

    1.Subject通过一个容器保存零到多个Observer. 2.Subject通过Add,Delete方法调整Observer. 3.Subject的notifyObservers方法实际是逐个调用 ...

  2. Java设计模式(20)观察者模式(Observer模式)

    Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循 ...

  3. 23种设计模式 - 组件协作(TemplateMethod - Observer/Event - Strategy)

    其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 组件协作 现代软件专业分工之后的第一个结果是"框架与应用程序的划分","组件 ...

  4. 设计模式之——Observer模式

    Observer模式又叫做观察者模式,当观察对象状态发生变化的时候,就会通知给观察者.这种模式适用于根据对象状态进行响应的场景! 实例程序是一个输出数字的程序. 观察者Observer类用于每500m ...

  5. php设计模式课程---4、观察者模式的好处是什么

    php设计模式课程---4.观察者模式的好处是什么 一.总结 一句话总结: 方便选择之后去控制监听的板块数:比如选择男士之后,我可以决定监听广告里面的第二和第三板块. 1.为什么有观察者模式? 错误理 ...

  6. C#设计模式之十六观察者模式(Observer Pattern)【行为型】

    一.引言 今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份)把所有的模式写完,2018年,新的一年写一些新的东西.今天我们开始讲“行为 ...

  7. 设计模式(二)The Observer Pattern 观察者模式

    问题引入 生成一个公告板显示当时的天气状况,当天气状况发生改变的时候公告板能够实时的更新. 模式定义 定义对象之间的一对多的依赖.当一个对象改变状态时,它的全部依赖者都会自己主动收到通知并自己主动更新 ...

  8. [Android&amp;Java]浅谈设计模式-代码篇:观察者模式Observer

    观察者,就如同一个人,对非常多东西都感兴趣,就好像音乐.电子产品.Game.股票等,这些东西的变化都能引起爱好者们的注意并时刻关注他们.在代码中.我们也有这种一种方式来设计一些好玩的思想来.今天就写个 ...

  9. 【设计模式 - 19】之观察者模式(Observer)

    1      模式简介 观察者模式的介绍: 观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象的状态发生改变时,它的所有依赖者都会收到通知并自动更新. 发布者(被观察者) + 订阅者(观察者) ...

随机推荐

  1. [WinForm] 使用 WebBrowser 操作 HTML 頁面的 Element-摘自网络

    前言 在 Window Form 應用程式如果需要瀏覽網頁時可以崁入 WebBrowser 控制項,但如果需要操作崁入的 HTML 的網頁元素,就需要額外的操作,以下紀錄幾種操作 HTML 元素的方法 ...

  2. ipython, 一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数

    一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数. 若用的是fish s ...

  3. Matlab 图像画在坐标轴下

    >> x=linspace(,*pi,); >> y=sin(x); >> figure;plot(x,y,'r-') >> set(gca,'xaxi ...

  4. HD1049Climbing Worm

    Problem Description An inch worm is at the bottom of a well n inches deep. It has enough energy to c ...

  5. POJ 3449 Geometric Shapes(判断几个不同图形的相交,线段相交判断)

    Geometric Shapes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1243   Accepted: 524 D ...

  6. beantool.map2Bean(map,obj)

  7. 如何在C#中模拟C++的联合(Union)?[C#, C++] How To Simulate C++ Union In C#?

    1 什么是联合? 联合(Union)是一种特殊的类,一个联合中的数据成员在内存中的存储是互相重叠的.每个数据成员都在相同的内存地址开始.分配给联合的存储区数量是“要包含它最大的数据成员”所需的内存数. ...

  8. 【Netbeans】表格的使用

    参数两个:一个二维数组赋予数据,一个一位数组赋予属性名

  9. JAVA 开发实例 一 移动的小球

    package com.java.move; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; i ...

  10. JS:中文GB2312编码

    今天开发遇到了个问题,有点纳闷.... 在ajax的时候要传递一个中文值,不管我在js中是否使用了encodeURI.encodeURIComponent编码,但是在后台request获取的值是始终是 ...