天天看點

用c++實作《圖解設計模式》——observer模式(老師說這個很常用)

他是MVC的重要組成部分

動機

用c++實作《圖解設計模式》——observer模式(老師說這個很常用)

模式定義

用c++實作《圖解設計模式》——observer模式(老師說這個很常用)

結構

用c++實作《圖解設計模式》——observer模式(老師說這個很常用)
用c++實作《圖解設計模式》——observer模式(老師說這個很常用)

對于一個實作檔案切割的類,如果需要對其增加一個顯示進度條的選項,使得使用者可以知道檔案切割的進度。如果直接在類中增加一個字段來表示進度條,這種修改方式好不好?

代碼如下(代碼都是僞代碼:

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++;
		}
	}
};           

總結

用c++實作《圖解設計模式》——observer模式(老師說這個很常用)

————————————————————————————————————————————————————————

來自《圖解設計模式》的補充

觀察者模式,當觀察對象發生變化時,會通知給觀察者。

觀察者模式适用于根據對象狀态而做出相應處理的場景。

繼續閱讀