软件构建过程中,需要为某些对象建立一种“通知依赖关系”--一个对象(目标对象)的状态发生变化,所有的依赖对象(观察者对象)都将得到通知
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖它的对象都得到通知并自动更新。
文件分割器
class FileSplitter{
string m_filePath;
int m_fileNumber;
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++){
//...
}
}
};
// 界面
class MainForm : public Form{
TextBox* txtFilePath;
TextBox* txtFileNumber;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
FileSplitter splitter(filePath, number);
splitter.split();
}
};
需求:提供进度条进行展示
class FileSplitter{
string m_filePath;
int m_fileNumber;
+ ProgressBar* m_progressBar; // 通知控件
public:
- FileSplitter(const string& filePath, int fileNumber) :
- m_filePath(filePath), m_fileNumber(fileNumber){ }
+ 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){
+ m_progressBar->setValue((i+1) / m_fileNumber); // 更新进度条
+ }
}
}
};
// 界面
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);
+ FileSplitter splitter(filePath, number, progressBar);
splitter.split();
}
};
以上实现存在的问题:违反依赖倒置原则
- FileSplitter 依赖 ProgressBar(实现细节)
需求:提供进度条进行展示
+class IProgress{
+public:
+ virtual void DoProgress(float value) = 0;
+ virtual void ~IProgress(){}
+};
class FileSplitter{
string m_filePath;
int m_fileNumber;
+ IProgress* m_iprogress; // 抽象通知机制
public:
- FileSplitter(const string& filePath, int fileNumber) :
- m_filePath(filePath), m_fileNumber(fileNumber){ }
+ 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:
+ void onProgress(float value){
+ if(m_iprogress != nullptr){
+ m_iprogress->DoProgress(value); // 更新进度条
+ }
+ }
};
// 界面
-class MainForm : public Form{
+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);
+ FileSplitter splitter(filePath, number, this);
splitter.split();
}
+ void DoProgress(float value){
+ progressBar->setValue(value);
+ }
};
+class IProgress{
+public:
+ virtual void DoProgress(float value) = 0;
+ virtual void ~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 addIProgress(IProgress* iprogress){
+ m_iprogressList.push_back(iprogress);
+ }
+ void removeIProgress(IProgress* iprogress){
+ m_iprogressList.pop_back(iprogress);
+ }
void split(){
//1. 读取大文件
//2. 分批次向小文件中写入
for(int i = 0; i < m_fileNumber; i++){
//...
+ float progressValue = m_fileNumber;
+ progressValue = (i+1) / progressValue
+ onProgress(progressValue);
}
}
+protected:
+ void onProgress(float value){
+ list<IProgress*>::iterator itor = m_iprogressList.begin();
+ while(itor != m_iprogressList.end()){
+ itor->DoProgress(value);
+ itor++;
+ }
+ }
};
// 界面
-class MainForm : public Form{
+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);
}
+ void DoProgress(float value){
+ progressBar->setValue(value);
+ }
};
+class ConsoleNotifier : public IProgress{
+public:
+ virtual void DoProgress(float value){
+ cout << ".";
+ }
};