他是MVC的重要組成部分
動機
模式定義
結構
對于一個實作檔案切割的類,如果需要對其增加一個顯示進度條的選項,使得使用者可以知道檔案切割的進度。如果直接在類中增加一個字段來表示進度條,這種修改方式好不好?
代碼如下(代碼都是僞代碼:
mainform.cpp
//一個實作檔案分割器是類
class MainForm : public Form
{
//檔案路徑
TextBox* txtFilePath;
//使用者希望分隔的檔案個數
TextBox* txtFileNumber;
//進度條
ProgressBar* progressBar;
public:
//點選按鈕,我們會收集檔案資訊,然後去調用filespliter
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;
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);
}
}
};
直接在類裡面添加字段,這樣是不合适的,違背了八大設計原則。如果以後的需求還有變更,還繼續對類進行修改嗎?這樣是不對的。
依賴倒置原則給我們的一個說法——不要去依賴A,而是我依賴A的抽象基類。
但是單純的按照找父類的方式去尋找,你會發現走進了死胡同。因為fliesplitter.cpp檔案實作進度條繪制的函數setValue()在ProgressBar的父類中并不存在。是以,單純找基類是一個很粗淺的認識,
仔細分析你會發現,ProgressBar在類中扮演的角色是依賴通知。
對于通知,其實我們可以用相對抽象的方式來表達通知,而不是具體控件來表達通知。(下面代碼中添加了一個IProgress這樣一抽象接口類,來解開耦合性)
tip:c++的多繼承容易引發一些耦合性的問題,但是如果子類都隻是實作一個個的接口,這樣的多繼承還是很推薦的。
根據依賴倒置原則修改後的代碼
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,因為mainForm和progressBar本身就是一體的
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++;
}
}
};
總結
————————————————————————————————————————————————————————
來自《圖解設計模式》的補充
觀察者模式,當觀察對象發生變化時,會通知給觀察者。
觀察者模式适用于根據對象狀态而做出相應處理的場景。