定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。 ——《 设计模式》 GoF
以下是一个文件分割器的代码,txtFilePath为文件路径,txtFileNumber为需要把原文件分割的个数。 需求:由于文件很大的时候需要时间很长,因此想要代码能够提供一个进度条来展示文件分割的进度
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++){ //... float progressValue = m_fileNumber; progressValue = (i + 1) / progressValue; m_progressBar->setValue(progressValue); //更新进度条 } } };以上代码的更改违背了 面向对象的一个设计原则——依赖倒置原则(DIP)(①高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。②抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。)m_progressBar产生了编译依赖(A依赖于B,即A编译时B需要存在才能编译通过),它是实现细节,进度通知可能会有各种不同的显示,实现细节非常容易变化,
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 << "."; } }; 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++; } };