動機(Motivation)
- 在軟件構建過程中,我們需要爲某些對象建立一種“通知依賴關係” ——一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關係過於緊密,將使軟件不能很好地抵禦變化。
- 使用面向對象技術,可以將這種依賴關係弱化,並形成一種穩定的依賴關係。從而實現軟件體系結構的鬆耦合。
模式定義
- 定義對象間的一種一對多(變化)的依賴關係,以便當一個對象(Subject)的狀態發生改變時,所有依賴於它的對象都得到通知並自動更新。
要點總結
- 使用面向對象的抽象,Observer模式使得我們可以獨立地改變目標與觀察者,從而使二者之間的依賴關係達致鬆耦合。
- 目標發送通知時,無需指定觀察者,通知(可以攜帶通知信息作爲參數)會自動傳播。
- 觀察者自己決定是否需要訂閱通知,目標對象對此一無所知。
- Observer模式是基於事件的UI框架中非常常用的設計模式,也是MVC模式的一個重要組成部分。
示例
示例1:(不使用Observer模式)
subject1.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); // 更新進度條
}
}
};
mainObserver1.cpp
class mainObserver : 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();
}
};
分析:
mainObserver(觀察者) 傳遞參數 procgressBar 到 FileSplitter(被觀察者),FileSplitter(被觀察者)執行 procgressBar 達到通知的效果。其中,procgressBar 是實現細節(有可能是通過進度條顯示進度,也有可能通過控制檯輸出或者其他的方式,它是變化的)。而高層模塊 FileSplitter (穩定)依賴低層模塊 mainObserver(變化),只有低層模塊實現了,高層模塊才能正常編譯,違背了依賴倒置原則。
示例2:(使用Observer模式,一對一)
subject2.cpp
class FileSplitter // 被觀察者
{
string m_filePath;
int m_fileNumber;
// ProgressBar* m_progressBar; // 具體通知控件
IProgress* m_iprogress;//抽象通知機制
public:
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++){
if(miprogress l=nullptr){
float progressValue=m_fileNumber;
progressValue=(i+1)r/progressValue;
m_iprogress->DoProgress(progressValue);//更新進度條
}
}
}
};
observer2.cpp
class IProgress{ // 觀察者的抽象類
public: virtual void DoProgress(float value)=0;
virtual~IProgress(){};
}
mainObserver2.cpp
class mainObserver : 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);
splitter.split();
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
分析:
通過構造一個抽象類(穩定),使得高層模塊 FileSplitter (穩定)和低層模塊 mainObserver(變化)都指向它,滿足了依賴倒置原則,隔離了變化。
示例3:(使用Observer模式,一對多)
subject3.cpp
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++;
}
}
};
observer3.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
mainObserver3.cpp
class mainObserve : 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);
}
};
consoleObserver3.cpp
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
分析:
創建了一個 List 存放觀察者們,同時針對 List 增加了訂閱和取消訂閱的功能
參考鏈接: