|
三、观察者模式
2. 动机与背景
- 文件分割器。大文件需要分成若干个小文件,这其中需要进行进度展示(或者其他的类似场景),但是需要的进度展示不是一个,而是多个。
3. 没有使用设计模式的代码
#include <iostream>
using namespace std;
class FileSplitter {
private:
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar;
public:
FileSplitter(string& filePath, int fileNumber, ProgressBar* progressBar):
m_filePath(filePath),
m_fileNumber(fileNumber),
m_progressBar(progressBar) {
}
void split() {
for (int i = 0; i < m_fileNumber; ++i) {
m_progressBar->setValue();
}
}
};
// 主逻辑
class Form {};
class TextBox {
public:
string getText() {
return &#34;&#34;;
}
};
class ProgressBar {
public:
void setValue() {}
};
class MainForm: Form {
private:
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void buttonClick() {
string filePath = txtFilePath->getText();
int fileNumber = atoi(txtFileNumber->getText().c_str());
FileSplitter* splitter = new FileSplitter(filePath, fileNumber, progressBar);
splitter->split();
}
};
4. 存在的问题
- 观察者会变化,需要更改目标的通知实现方式,对于观察者增减的需求不满足开闭原则;通知的具体实现在目标处完成,没有独立抽象,不符合DIP原则。
5. 解决方法
- 使用观察者模式:定义对象间的一对多的关系,以便一个对象发生变化时,能够及时通知所依赖的其他对象(观察者),方便添加或删除观察者,并且符合DIP原则。
6. 使用设计模式的代码
#include <iostream>
#include <vector>
using namespace std;
class IProgress {
public:
virtual void DoProgress(float value) = 0;
virtual ~IProgress() {}
};
class FileSplitter {
private:
string m_filePath;
int m_fileNumber;
// 抽象通知机制
IProgress* m_iprogress;
// 多个观察者使用容器保存
vector<IProgress*> m_iprogressVector;
public:
FileSplitter(string& filePath, int fileNumber, IProgress* iprogress):
m_filePath(filePath),
m_fileNumber(fileNumber),
m_iprogress(iprogress) {
}
void split() {
for (int i = 0; i < m_fileNumber; ++i) {
m_iprogress->DoProgress(i);
// 多个观察者使用容器保存
}
}
// 多个观察者
void add() {}
void remove() {}
};
// 主逻辑
class Form {};
class TextBox {
public:
string getText() {
return &#34;&#34;;
}
};
class ProgressBar {
public:
void setValue() {}
};
class MainForm: public Form, public IProgress {
private:
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void buttonClick() {
string filePath = txtFilePath->getText();
int fileNumber = atoi(txtFileNumber->getText().c_str());
// this比较重要
FileSplitter* splitter = new FileSplitter(filePath, fileNumber, this);
splitter->split();
}
// 不论有多少个观察者,都可以实现自己的通知机制
virtual void Doprogress(float value) {
progressBar->setValue();
}
};
7. 适用场景
- 通知事件时,被通知的对象(观察者)经常变化,但不需要频繁改变目标的通知方式。
8. 总结
- 可以独立改变目标与观察者(松耦合)
- 目标发送通知时,无需指定观察者,只管通知机制,而通知自动传播
- 观察者自己决定是否订阅通知
9. 依赖
- 编译时依赖,在编译A之前必须完成B的编译,我们就说A依赖于B
- 运行时依赖
|
|