设计模式-观察者模式

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();
}
};

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++){
//...
if (m_progressBar != nullptr){
progressBar->setValue( (i + 1) / m_fileNumber); // 更新进度条
}
}
}
};

违背了依赖倒置设计原则


class IProgress{
public:
virtual void DoProgress(float value) = 0;
virtual ~IProgress(){}
};

class FileSplitter{
string m_filePath;
int m_fileNumber;
//ProgressBar* m_progressBar; // 具体通知控件
IProgress* m_iprogress; //抽象通知机制
public:
FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress): m_filePath(filePath),
m_fileNumber(fileNumber),
m_iprogress(iprogress){
}
void split(){
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);
}
}
protected:
virtual void onProgress(float value){
if (m_iprogress != nullptr){
m_iprogress->DoProgress(progressValue); // 更新进度条
}
}
};

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没有耦合任何一个界面类,就可以独立编译,在哪里都能支持进度通知。

但仍存在问题:不好支持多个通知。

要支持多个观察者,现在只支持一个观察者(MainForm或ProgressBar)。


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 {
virtual void DoProgress(float value) {
cout << "." <<;
}
};

class FileSplitter{
string m_filePath;
int m_fileNumber;
//ProgressBar* m_progressBar; // 具体通知控件
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.add(iprogress);
}
void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
protected:
virtual void onProgress(float value){
List<IProgress*>::iterator it = m_iprogressList.begin();
while(it != m_iprogressList.end() ){
(*it)->DoProgress(value); // 更新进度条
it++;
}
}
};


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



Observer:IProgress Update:DoProgress Attach:addIProgress Detach:removeIProgress Notify:onProgress ConcreteSubject:FileSplitter
ConcreteObserver:MainForm和ConsoleNotifier
Observer和Subject是稳定的。下面具体的是变化的。


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

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

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

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

相关推荐
咖啡八杯19 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
槑有老呆1 天前
从 Prompt Engineering 到 Harness Engineering:AI 编程的下一次跃迁
设计模式
HjhIron1 天前
从Prompt到Context:大模型应用开发的范式转移
设计模式·aigc·ai编程
咖啡八杯3 天前
GoF设计模式——中介者模式
java·后端·spring·设计模式
胡萝卜术3 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
亦暖筑序4 天前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
青禾网络6 天前
Web 前端如何接入 AI 音效生成:从零到可用的完整方案
人工智能·设计模式
ZJPRENO7 天前
吃透软件开发六大设计原则,告别烂代码
设计模式
咖啡八杯7 天前
GoF设计模式——命令模式
java·设计模式·架构
花椒技术8 天前
HJPusher / HJPlayer SDK 实践:我们为什么把直播推播链路拆成一套可复用能力
设计模式·harmonyos·直播