用c++实现《图解设计模式》——observer模式(老师说这个很常用)
他是MVC的重要组成部分
动机
模式定义
结构
对于一个实现文件切割的类,如果需要对其增加一个显示进度条的选项,使得用户可以知道文件切割的进度。如果直接在类中增加一个字段来表示进度条,这种修改方式好不好?
代码如下(代码都是伪代码:
mainform.cpp
//一个实现文件分割器是类
class MainForm : public Form
{
//文件路径
TextBox* txtFilePath;
//用户希望分隔的文件个数
TextBox* txtFileNumber;
//进度条
ProgressBar* progressBar;
public:
//点击按钮,我们会收集文件信息,然后去调用filespliter
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
FileSplitter splitter(filePath, number, progressBar);
splitter.split();
}
};
对应的实现代码如下:
FileSplitter.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 = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
//更新进度条的进展
m_progressBar->setValue(progressValue);
}
}
};
直接在类里面添加字段,这样是不合适的,违背了八大设计原则。如果以后的需求还有变更,还继续对类进行修改吗?这样是不对的。
依赖倒置原则给我们的一个说法——不要去依赖A,而是我依赖A的抽象基类。
但是单纯的按照找父类的方式去寻找,你会发现走进了死胡同。因为fliesplitter.cpp文件实现进度条绘制的函数setValue()在ProgressBar的父类中并不存在。因此,单纯找基类是一个很粗浅的认识,
仔细分析你会发现,ProgressBar在类中扮演的角色是依赖通知。
对于通知,其实我们可以用相对抽象的方式来表达通知,而不是具体控件来表达通知。(下面代码中添加了一个IProgress这样一抽象接口类,来解开耦合性)
tip:c++的多继承容易引发一些耦合性的问题,但是如果子类都只是实现一个个的接口,这样的多继承还是很推荐的。
根据依赖倒置原则修改后的代码
mainform.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,因为mainForm和progressBar本身就是一体的
progressBar->setValue(value);
}
};
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
FileSplitter.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
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 = 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++;
}
}
};
总结
————————————————————————————————————————————————————————
来自《图解设计模式》的补充
观察者模式,当观察对象发生变化时,会通知给观察者。
观察者模式适用于根据对象状态而做出相应处理的场景。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
深度学习Trick——用权重约束减轻深层网络过拟合|附(Keras)实现代码
在深度学习中,批量归一化(batch normalization)以及对损失函数加一些正则项这两类方法,一般可以提升模型的性能。这两类方法基本上都属于权重约束,用于减少深度学习神经网络模型对训练数据的过拟合,并改善模型对新数据的性能。 目前,存在多种类型的权重约束方法,例如最大化或单位向量归一化,有些方法也必须需要配置超参数。 在本教程中,使用Keras API,用于向深度学习神经网络模型添加权重约束以减少过拟合。 完成本教程后,您将了解: 如何使用Keras API创建向量范数约束; 如何使用Keras API为MLP、CNN和RNN层添加权重约束; 如何通过向现有模型添加权重约束来减少过度拟合; 下面,让我们开始吧。 本教程分为三个部分: Keras中的权重约束; 图层上的权重约束; 权重约束
-
下一篇
《快学 Go 语言》第 10 课 —— 错误与异常
import "os"import "fmt" ..... 在这段代码里有几个点需要特别注意。第一个需要注意的是 os.Open()、f.Read() 函数返回了两个值,Go 语言不但允许函数返回两个值,三个值四个值都是可以的,只不过 Go 语言普遍没有使用多返回值的习惯,仅仅是在需要返回错误的时候才会需要两个返回值。除了错误之外,还有一个地方需要两个返回值,那就是字典,通过第二个返回值来告知读取的结果是零值还是根本就不存在。 var score, ok := scores["apple"] 第二个需要注意的是 defer 关键字,它将文件的关闭调用推迟到当前函数的尾部执行,即使后面的代码抛出了异常,文件关闭也会确保被执行,相当于 Java 语言的 finally 语句块。defer 是 Go 语言非常重要的特性,在日常应用开发中,我们会经常使用到它。 第三个需要注意的地方是 append 函数参数中出现了 … 符号。在切片章节,我们知道 append 函数可以将单个元素追加到切片中,其实 append 函数可以一次性追加多个元素,它的参数数量是可变的。 var s = []int{1...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Hadoop3单机部署,实现最简伪集群
- MySQL数据库在高并发下的优化方案