C++设计模式<五>:Observe观察者模式
来源:互联网 发布:红蜘蛛控制软件 编辑:程序博客网 时间:2024/04/29 16:14
1.动机
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好滴抵御变化
2.模式定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(object)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
3.例子
看一个文件分割器的例子(以前磁盘小,经常需要文件分割)
class FileSplitter{ string m_filePath; //文件路径 int m_fileNumber; //文件个数 ProgressBar* m_progressBar;//需求更改而添加的public: FileSplitter(const string& filePath, int fileNumber) : m_filePath(filePath), m_fileNumber(fileNumber), { }/*改动之后,构造函数也得改,这里就不写了*/ void split(){ //1.读取大文件 //2.分批次向小文件中写入 for (int i = 0; i < m_fileNumber; i++){ //... float progressValue = m_fileNumber; progressValue = (i + 1) / progressValue; m_progressBar->setValue(progressValue); } }};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);改动之前 FileSplitter splitter(filePath, number,progressBar);//改动之后 splitter.split(); }};
分析:
这是程序刚开始的时候,随着需求改变,需要加入进度条,因此自然会这样更改(加一个通知控件,见程序标识),看下这么更改有什么问题(结合8大原则见C++面向对象原则),违反了依赖倒置原则(明显FileSplitter依赖于ProgressBar(这个就是个细节,就是说可能有很多不同的进度展示))。那怎么办?可能根据以前的经验会想到去找ProgressBar的基类,但是如果其基类都没有setValue这个方法呢?这里就得考虑扮演ProgressBar什么角色,是一个通知。因此我们可以抽象的来表示这个通知,而不是具体用一个控件来表示。因此可以这样更改,如下
class IProgress{public: virtual void DoProgress(float value)=0; virtual ~IProgress(){}}; //表达一种抽象的通知机制class FileSplitter{ string m_filePath; int m_fileNumber; //ProgressBar* m_progressBar;具体通知控件 IProgress* m_iprogressList; // 抽象通知机制,这个是设计的最大亮点!!!public: FileSplitter(const string& filePath, int fileNumber, Iprogress* iProgress) : m_filePath(filePath), m_fileNumber(fileNumber){ } void split(){ //1.读取大文件 //2.分批次向小文件中写入 for (int i = 0; i < m_fileNumber; i++){ //... float progressValue = m_fileNumber; progressValue = (i + 1) / progressValue; onProgress(progressValue);//发送通知 } } virtual void onProgress(float value){ if(m_iprogress!=NULL) m_iprogress->doProgress();};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()); FileSplitter splitter(filePath, number,this); splitter.split(); } virtual void DoProgress(float value){ progressBar->setValue(value); }};
这样更改后,可以看到FileSplitter就没有依赖一个界面类了,可以独立编译等。这样写完后,以后可以把FileSplitter类放到一个windows界面,linux界面,控制台都行。
当然如果要实现多个观察者呢?(上文的例子MainForm就是一个观察则),比如说增加一个控制台进度。
可以这样更改
class MainForm : public Form, public IProgress //不推荐多继承,只有一种推荐:一个是主的继承类,其他都是接口{ TextBox* txtFilePath; TextBox* txtFileNumber; IProgress* m_iprogress;//通知机制public: void Button1_Click(){ string filePath = txtFilePath->getText(); int number = atoi(txtFileNumber->getText().c_str()); ConsoleNotifier cn;//添加的 FileSplitter splitter(filePath, number,&cn);//问题所在,发现只能添加一个观察者 splitter.split(); } virtual void DoProgress(float value){ progressBar->setValue(value); }};class ConsoleNotifier : public IProgress {public: virtual void DoProgress(float value){ cout << "."; }};
发现问题:只能有一个观察者。为了支持多个观察者,如下
class IProgress{public: virtual void DoProgress(float value)=0; virtual ~IProgress(){}}; //表达一种抽象的通知机制//FileSplitter改变完后,就没有耦合一个界面类了,可以放到界面,也可以放到控制台中等,这个进度通知是通过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 = 0; i < m_fileNumber; i++){ //... float progressValue = m_fileNumber; progressValue = (i + 1) / 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++; } }};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 << "."; }};
以上才是真正的观察者模式。
4.结构
那么以上稳定的是:Observe,Subject,是系统可以依赖的;而剩下的是变化部分。
5.要点总结
- 使用面向对象的抽象,Observe模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合
- 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播
- 观察者自己决定是否需要订阅通知,目标对象对此一无所知
- Observe模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分
- C++设计模式<五>:Observe观察者模式
- 设计模式---observe 模式
- 设计模式 - 观察者模式(Observe pattern)C++实现
- 设计模式-Observe
- 【设计模式】【五】观察者模式
- 【设计模式 五】观察者模式
- 【设计模式之五:观察者模式】观察者模式浅析
- .NET中的设计模式五:观察者模式
- .NET中的设计模式五:观察者模式
- .NET中的设计模式五:观察者模式 .
- 设计模式之五 观察者模式
- 设计模式(五)观察者模式
- 大话设计模式(五)观察者模式
- 设计模式(五)观察者模式
- 设计模式--观察者模式(五)
- Java设计模式之五:观察者模式
- 设计模式(五)观察者模式
- 设计模式--观察者模式(C++)
- 【计算机网络】:如何看懂路由表
- java.lang.RuntimeException: Binary XML file line #52: You must supply a layout_height attribute.
- 九度题目:矩阵转置(1193)
- Hark语音识别学习(二)--HARK数据类型
- 5种css图片浮动效果
- C++设计模式<五>:Observe观察者模式
- 算法复习-红黑树-c++实现
- 【机房重构】策略模式
- CSP考试 2014年09月第3题 字符串匹配 C语言实现
- 动态规划-流水线问题
- python os.path与路径相关的
- 题目1435:迷瘴
- linux挂载第二硬盘的lvm分区方法
- navigationBar导航条和navigationItem设置:基本搞定导航条上的文字和按钮以及各种跳转