Observer
有时候需要为某些对象建立“通知依赖关系”,一个对象(目标对象)的状态发生变化,所有的依赖对象(观察者对象)都将得到通知
实现一个文件分割器
// MainForm.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();
}
};
如果想要显示文件分割的进度条,就需要在文件分割的过程中更新进度条的数据,调用相应方法
// FileSplitter.cpp
class FileSplitter {
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar; // 依赖于MainForm的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);
}
}
};
改进
这里依赖于MainForm
,如果MainForm
要修改progressBar
,FileSplitter
也得修改,所以应该依赖于抽象,而不是实体,所以声明一个抽象类,定义了进度显示的接口
class IProgress{
public:
virtual void DoProgress(float value)=0; // 抽象类的接口
virtual ~IProgress(){}
};
然后让MainForm
继承这个抽象类,实现接口
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 << ".";
}
};
FileSplitter
里面可以添加多个进度显示对象(观察者),进度发生变化就通知这些观察者,也就是调用观察者对应的接口实现函数
list
里面放的是抽象类的指针,这样就可以利用多态调用不同子类的虚函数,这样就可以支持多种进度显示,而不是和特定的ProgressBar
绑定
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++;
}
}
};
这样FileSplitter
依赖于IProgress
,只需要调用其接口就可以了,不需要知道具体是怎么实现的,具体的接口实现是在MainForm
里面,如果要修改接口就直接在MainForm
里改就行了,FileSplitter
不用动
模式定义
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新
- 使用面向对象的抽象,Observer 模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系保持松耦合
- 目标发送通知时,无需指定观察者,通知会自动传播
- 观察者自己决定是否需要订阅通知,目标对象对此一无所知
- Observer 模式是基于事件的 UI 框架中非常常用的设计模式