設計模式 —— Observer 觀察者模式

動機(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 增加了訂閱和取消訂閱的功能

在這裏插入圖片描述


參考鏈接:

https://www.bilibili.com/video/av24176315/?p=5

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章