定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
——《设计模式》GoF
首先要做一个界面类,包括文件路径以及文件的分割个数,按钮点击时收集路径、个数,调用分割方法
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;
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++){
//...
比如说将文件分割成10个部分,用一个进度条展示
增加一个进度条控件
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);
}
}
};
违背依赖倒置原则:抽象(稳定)不应该依赖于实现细节(变化)。题中的文件分割器依赖进度条的细节,可能以后会被改为label等等不同的方式。依赖倒置原则告诉我们不要去依赖A而去依赖A的抽象基类,但是不能单纯找基类。
分析改进思路:m_progressBar是一个控件来进行通知,我们可以用抽象的方式来通知而不依赖具体控件
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* m_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){
//virtual可供子类改写
if(m_iprogress!=nullptr)
m_iprogress->DoProgress(value);
}
};
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);//当前mainform对象指针,实现IProgress接口
splitter.split();
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
此时文件分割类不再耦合界面类,可以独立编译,本需求解决
首先新设计一个ConsoleNotifier
类管理所有通知,实现IProgress
接口
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
为了支持多个可以将IProgress* m_iprogress;
用链表装入List
,那么就需要对链表操作插入与删除
void addIProgress(IProgress* iprogress){
m_iprogressList.push(iprogress);
}
void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
对于更新进度条需要拿到链表迭代器逐个更新
virtual void onProgress(float value){
List<IProgress*>::iterator itor=m_iprogressList.begin();
while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value); //更新进度条
itor++;
}
}
文件分割器
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++;
}
}
};
界面代码如下
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 << ".";
}
};