东营微信网站制作,网页设计html成品免费,南通网站上百度首页,网站建设模板犀牛云1、定义与动机
观察者模式定义#xff1a;定义对象间的一种1对多#xff08;变化#xff09;的依赖关系#xff0c;以便当一个对象#xff08;Subject#xff09;的状态发生比改变时#xff0c;所有依赖于它的对象都得到通知并且自动更新
再软件构建过程中#xff0c…1、定义与动机
观察者模式定义定义对象间的一种1对多变化的依赖关系以便当一个对象Subject的状态发生比改变时所有依赖于它的对象都得到通知并且自动更新
再软件构建过程中我们需要为某些对象建立一种“通知依赖关系“——一个对象目标对象的状态发生改变所有的依赖对象观察者对象都将得到通知。如果这样的依赖关系过于紧密僵尸软件不能很好地抵御变化。使用面向对象技术可以将这种依赖关系弱化并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
2、举例分析
例如点击button将一个文件分割成指定数量的功能在分割过程中一般有一个合理的类似于进度条的通知功能可以看到当前分割的进度很容易写出下面的代码 MainForm在收到触发button按钮的操作之后拿到文件名、需求文件个数就调用FileSplitter进行分割FileSplitter类中依赖一个ProgressBar控件然后在split分割的过程中计算当前分割的进度将值会写到ProgressBar控件中用于同步展示 但是这样写并不好 首先可以思考到的一个问题ProgressBar写死了我们只能使用这个控件来实现如果想换个控件、换个方式或者多个控件来展示似乎不太可行违背了开闭原则其次很重要的一点FileSplitter是一个高层模块 ProgressBar是一个低层模块形成了高层依赖低层模块。而依赖倒置原则讲的是高层和低层模块都应该依赖其抽象抽象不能依赖实现细节实现细节应该依赖抽象。ProgressBar实际是一个实现细节设置value就能展示而这样的一个依赖会产生问题如果换个形式进度条展示需要更改需求时很难做到难以抵御变化。
class FileSplitter{
private:string filePath;int fileNumber;ProgressBar* progressBar;
public:FileSplitter(string filepath, int number): filePath(filepath), fileNumber(number){}void split(){// 1. 分批次读取文件大小// 2. 分批次向小文件写入for(int i 0;i fileNumber;i){// 处理...// 展示处理的进度条progressBar-setValue(1.0*(i1) / fileNumber);}}
};class MainForm: public Form{
private:TextBox* txtFilepath;TextBox* txtFileNumber;
public:void button_event_split(){string filePath txtFilepath-getText();int number atoi(txtFilepath-getText().c_str());FileSplitter fileSplitter(filePath, number);fileSplitter.split();}
};3、观察者模式
通过分析可知进度条的展示功能不应该出现在文件分割功能里应该通知依赖它的地方进行进度条展示它只需要向外发送通知即可至于外面进行如何实现这个进度条展示或者其他功能并不需要它关心观察者模式的核心就是向外进行通知外面收到通知的对象如何实现就由它们自己决定因此观察者模式来做这一需求对上面代码进行改造可以变成如下形式
3.1、基础优化一
class IProgress{
public:virtual void doProgress(double value) 0;virtual ~IProgress(){}
};class FileSplitter{
private:string filePath;int fileNumber;IProgress *iProgress;
public:FileSplitter(string _filepath, int _number, IProgress *_iProgress): filePath(_filepath), fileNumber(_number), iProgress(_iProgress){}void split(){// 1. 分批次读取文件大小// 2. 分批次向小文件写入for(int i 0;i fileNumber;i){// 处理...// 展示处理的进度条iProgress-doProgress(1.0*(i1) / fileNumber);}}
};class MainForm: public Form, public IProgress{
private:TextBox* txtFilepath;TextBox* txtFileNumber;ProgressBar *progressBar;
public:void button_event_split(){string filePath txtFilepath-getText();int number atoi(txtFilepath-getText().c_str());FileSplitter fileSplitter(filePath, number, this);fileSplitter.split();}virtual void doProgress(double value){progressBar-setValue(value);}
};3.2、多个观察者二
当有多个观察者时可以通过如下的代码来实现
#include vector
class IProgress{
public:virtual void doProgress(double value) 0;virtual ~IProgress(){}
};class FileSplitter{
private:string filePath;int fileNumber;vectorIProgress* progressList;
public:FileSplitter(string _filepath, int _number, IProgress *_iProgress): filePath(_filepath), fileNumber(_number), iProgress(_iProgress){}void split(){// 1. 分批次读取文件大小// 2. 分批次向小文件写入for(int i 0;i fileNumber;i){// 处理...// 展示处理的进度条notify_observers(1.0*(i1) / fileNumber);}}void add_IProgress(IProgress* iProgress){progressList.push_back(iProgress);}void remove_IProgress(IProgress* iProgress){progressList.erase(iProgress);}
protected:void notify_observers(double value){for(auto it progressList.begin();it ! progressList.end();it){(*it)-doProgress(value)}}
};class MainForm: public Form, public IProgress{
private:TextBox* txtFilepath;TextBox* txtFileNumber;ProgressBar *progressBar;
public:void button_event_split(){string filePath txtFilepath-getText();int number atoi(txtFilepath-getText().c_str());FileSplitter fileSplitter(filePath, number, this);// 添加额外的观察者fileSplitter.add_IProgress(...);fileSplitter.split();}virtual void doProgress(double value){progressBar-setValue(value);}
};class ConsoleNotifier: public IProgress{virtual void doProgress(double value){//...具体实现...}
};4、总结
上面的举例代码中Subject对应FileSplitter、而下面的ConcreteSubject就是那个Vector容器以及加和删除的方法。Observer对应上面的进度条类型ConcreteObserver对应MainForm 使用面向对象的抽象Observer模式使得我们可以独立地改变目标与观察者从而使二者之间的依赖关系达到松耦合目标发送通知时无需指定观察者通知可以携带通知信息做为参数会自动传播观察者自己决定是否需要订阅通知目标对象对此一无所知Observer模式是基于事件的UI框架中非常常用的设计模式也是MVC模式的重要组成部分