徐汇网站开发,怎么添加字体在wordpress,河北百度推广电话,展览设计制作公司接上篇#xff0c;本篇将会介绍C设计模式中的Observer 观察者模式#xff0c;和前2篇模板方法Template Method及Strategy 策略模式一样#xff0c;仍属于“组件协作”模式。Observer 在某些领域也叫做 Event 。 文章目录 1. 动机#xff08; Motivation#xff09;2. 代码…接上篇本篇将会介绍C设计模式中的Observer 观察者模式和前2篇模板方法Template Method及Strategy 策略模式一样仍属于“组件协作”模式。Observer 在某些领域也叫做 Event 。 文章目录 1. 动机 Motivation2. 代码演示Observer 观察者模式2.1 常用处理方法2.1.1 MainForm1.cpp2.1.2 FileSplitter1.cpp 2.2 Observer 观察者模式2.2.1 FileSplitter2.cpp2.2.2 MainForm2.cpp 3. 模式定义4. 结构 Structure5. 要点总结 1. 动机 Motivation
在软件构建过程中我们需要为某些对象建立一种“通知依赖关系” ——一个对象目标对象的状态发生改变所有的依赖对象观察者对象都将得到通知。如果这样的依赖关系过于紧密将使软件不能很好地抵御变化。使用面向对象技术可以将这种依赖关系弱化并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
2. 代码演示Observer 观察者模式
假设以下的场景需求做一个文件的分割器。虽然现在文件分割器使用的比较少但是在之前是使用很广泛的因为当时还是一个3寸盘的时代经常需要将大文件拷走就需要将大的文件分隔为多个文件拷贝携带走。 下面是一个伪码只会展示主干部分。
2.1 常用处理方法
首先需要一个界面mainform就是一个windows界面父类为Form主要有两个控件txtFilePath大文件的全路径和txtFileNumber希望分割的文件个数此处给出的是后期修改后的代码。 Button1_Click函数中收集用户输入的2个参数信息传递给FileSplitter splittersplitter调用split()
2.1.1 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();}
};
2.1.2 FileSplitter1.cpp
FileSplitter中放文件变量m_filePath负责文件路径m_fileNumber负责文件个数通过构造器给这些成员变量赋值。
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 0; i m_fileNumber; i){//...float progressValue m_fileNumber;progressValue (i 1) / progressValue;m_progressBar-setValue(progressValue);}}
};上面代码就实现了在Button1_Click时的文件的分割功能。
假设有一个用户需求希望进行文件分割时如果文件特别大需要分割很长时间这个时候需要提供一个进度条进行进度展示。 最朴素的想法就是在界面中提供一个ProgressBar* progressBar并且在FileSplitter中提供方法代码结果如上。
但是上面的实现方式是否违背了某一设计原则呢即违背依赖倒置原则(DIP)
高层模块(稳定)不应该依赖于低层模块(变化)二者都应该依赖于抽象(稳定) 。抽象(稳定)不应该依赖于实现细节(变化) 实现细节应该依赖于抽象(稳定)。
上面实现中一般我们所讲的依赖就是编译式依赖除非明确提出运行式依赖例如A依赖B也就是A编译时B必须存 在FileSplitter中的ProgressBar* progressBar就产生了编译式依赖这个ProgressBar* progressBar就是依赖倒置原则(DIP)中讲到的实现细节为什么说它是实现细节呢因为进度的显示方式可能会有变化这就带了实现细节层面变更的困扰
2.2 Observer 观察者模式
需要对上面的代码进行重构分析可以看到ProgressBar* progressBar实际扮演的是一个通知可以使用一种抽象的方式来表达一个通知而不需要具体的控件表达通知。
2.2.1 FileSplitter2.cpp
class IProgress{
public:virtual void DoProgress(float value)0;virtual ~IProgress(){}
};class FileSplitter
{string m_filePath;int m_fileNumber;ListIProgress* 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){ListIProgress*::iterator itorm_iprogressList.begin();while (itor ! m_iprogressList.end() )(*itor)-DoProgress(value); //更新进度条itor;}}
};2.2.2 MainForm2.cpp
C中支持多继承一般不推荐使用多继承的方式可能会导致复杂的耦合性问题但是C推荐一种多继承的形式就是一个是主继承类(如public Form)其他是接口或者抽象基类public IProgress。
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 .;}
};
3. 模式定义
定义对象间的一种一对多变化的依赖关系以便当一个对象(Subject)的状态发生改变时所有依赖于它的对象都得到通知并自动更新。 ——《设计模式》 GoF
4. 结构 Structure 上图是《设计模式》GoF中定义的Observer 观察者模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分也就是下图中红框和蓝框框选的部分。 GoF的设计模式中建议将addIProgressremoveIProgressonProgress放到父类中让FileSplitter去继承父类。而此处我们的框架是将其直接写到FileSplitter中不管是否提出Subject都是观察者模式本博文重构的代码就没有提出Subject其实是将Subject和ConcreteSubject合二为一。
5. 要点总结
使用面向对象的抽象 Observer模式使得我们可以独立地改变目标与观察者从而使二者之间的依赖关系达致松耦合。
代码中随便添加观察者但是addIProgressremoveIProgressonProgress保持复用性不变
目标发送通知时无需指定观察者通知可以携带通知信息作为参数会自动传播。
onProgress(progressValue);//发送通知不知道谁是观察者针对通知机制抽象通知
观察者自己决定是否需要订阅通知目标对象对此一无所知。
splitter.addIProgress(this); //订阅通知splitter.addIProgress(cn) //订阅通知
Observer模式是基于事件的UI框架中非常常用的设计模式也是MVC模式的一个重要组成部分。
Observer 观察者模式与模板方法Template Method是一样的常用。例如java中的listener就是观察者模式、C#中的Event也是观察者模式
Observer模式需要多思考最关键的是抽象的通知依赖关系。