1. 程式人生 > >C++設計模式——中介者模式

C++設計模式——中介者模式

前言

   在沒有引入星形網路拓撲圖之前,計算機之間可以直接通訊,但如果某臺計算機要和其它多臺計算機進行通訊,則這臺計算機必須認識所要進行通訊的其餘計算機。計算機與計算機之間存在多對多的關係,導致計算機之間關係非常複雜。引入星形網路拓撲圖之後,計算機之間不再直接進行通訊,而是由交換機進行處理。計算機不需要認識其它的計算機,只需要認識交換機就可以了。計算機之間的關係從多對多變成了一對多。在設計模式中也存在類似的模式,某些類/物件之間的相互呼叫關係錯綜複雜,可以使用中間類來協調這些類/物件之間的複雜關係,以降低系統的耦合度,稱之為中介者模式。

1、中介者模式概述

    如果在一個系統中物件之間的聯絡呈現為網狀結構,物件之間存在大量的多對多聯絡,將導致系統非常複雜,這些物件既會影響別的物件,也會被別的物件所影響,這些物件稱為同事物件

,它們之間通過彼此的相互作用實現系統的行為。在網狀結構中,幾乎每個物件都需要與其他物件發生相互作用,而這種相互作用表現為一個物件與另外一個物件的直接耦合,這將導致一個過度耦合的系統。由於物件之間過渡耦合,複用性極差;新增新的物件,則這個物件必須加入錯綜複雜的關係網,可擴充套件性差;對這個系統進行維護簡直是一場噩夢。

物件之間存在複雜關係的網狀結構

    中介者模式可以使物件之間的關係數量急劇減少,通過引入中介者物件,可以將系統的網狀結構變成以中介者為中心的星形結構。在這個星形結構中,同事物件不再直接與另一個物件聯絡,它通過中介者物件與另一個物件發生相互作用。中介者物件的存在保證了物件結構上的穩定,也就是說,系統的結構不會因為新物件的引入帶來大量的修改工作。

20-5 引入中介者物件的星型結構

    如果在一個系統中物件之間存在多對多的相互關係,我們可以將物件之間的一些互動行為從各個物件中分離出來,並集中封裝在一箇中介者物件中,並由該中介者進行統一協調,這樣物件之間多對多的複雜關係就轉化為相對簡單的一對多關係。通過引入中介者來簡化物件之間的複雜互動,中介者模式是“迪米特法則”的一個典型應用

中介者模式(Mediator Pattern):用一箇中介物件(中介者)來封裝一系列的物件互動,中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。中介者模式又稱為調停者模式,它是一種物件行為型模式。

    在中介者模式中,我們引入了用於協調其他物件/類之間相互呼叫的中介者類,為了讓系統具有更好的靈活性和可擴充套件性,通常還提供了抽象中介者。

中介者模式結構圖

    Mediator(抽象中介者):它定義一個介面,該介面用於與各同事物件之間進行通訊。

    ConcreteMediator(具體中介者):它是抽象中介者的子類,通過協調各個同事物件來實現協作行為,它維持了對各個同事物件的引用。

    Colleague(抽象同事類):它定義各個同事類公有的方法,並聲明瞭一些抽象方法來供子類實現,同時它維持了一個對抽象中介者類的引用,其子類可以通過該引用來與中介者通訊。

    ConcreteColleague(具體同事類):它是抽象同事類的子類;每一個同事物件在需要和其他同事物件通訊時,先與中介者通訊,通過中介者來間接完成與其他同事類的通訊;在具體同事類中實現了在抽象同事類中宣告的抽象方法。

    中介者模式的核心在於中介者類的引入,在中介者模式中,中介者類承擔了兩方面的職責:

    (1) 中轉作用(結構性):通過中介者提供的中轉作用,各個同事物件就不再需要顯式引用其他同事,當需要和其他同事進行通訊時,可通過中介者來實現間接呼叫。該中轉作用屬於中介者在結構上的支援。

    (2) 協調作用(行為性):中介者可以更進一步的對同事之間的關係進行封裝,同事可以一致的和中介者進行互動,而不需要指明中介者需要具體怎麼做,中介者根據封裝在自身內部的協調邏輯,對同事的請求進行進一步處理,將同事成員之間的關係行為進行分離和封裝。該協調作用屬於中介者在行為上的支援。

2、掃描助理影象預覽的設計與實現


掃描助理影象預覽模組: 當EditText文字框中預設資料夾改變,則重新整理掃描相簿中的資料夾,這些資料夾為當前預設資料夾目錄下的子資料夾,並且相簿中顯示掃描相簿中第一個資料夾下面的所有圖片; 當雙擊掃描相簿中的某個資料夾,則把該資料夾裡面的所有圖片顯示到相簿中; 當點選相簿中的某張圖片,則選中掃描相簿中該圖片所屬的資料夾。

    UIWindow視窗類為抽象同事類Colleague; EditText、ListCtrl、ListView三個控制元件類為具體同事類ConcreteColleague;  UIControl控制中心為具體中介者ConcreteMediator。
    UIControl控制中心(中介者)實現程式碼如下:
//UI控制中心(也就是中介者)
class UIControl
{
private:
	//中介者認識所有控制元件
	EditText * m_pEditText;
	ListCtrl * m_pListCtrl;
	ListView * m_pListView;
public:
	//設定文字控制元件、listCtrl、ListView控制元件
	void SetEditText(EditText * pEditText)
	{
		m_pEditText = pEditText;
	}

	void SetListCtrl(ListCtrl * pListCtrl)
	{
		m_pListCtrl = pListCtrl;
	}

	void SetListView(ListView * pListView)
	{
		m_pListView = pListView;
	}

	//文字框內容改變事件
        void OnChangeText()
	{
		m_pListCtrl->UpdateImage();
		m_pListView->UpdateFolder();
	}

	//ListCtrl控制元件單擊事件
	void OnListCtrlClick()
	{
		m_pListView->SelectFolder();
	}

	//ListViw控制元件雙擊事件
	void OnListViewDoubleClick()
	{
		m_pListCtrl->UpdateImage();
	}
};

UIWindow類是一個父類(抽象同事類),維持一個指向具體中介者的引用。實現程式碼如下:
//UI視窗類
class UIWindow
{
private:
	//UI視窗維持一個UIControl的引用(也就是中介者)
	UIControl * m_pUIControl;
public:
	//設定UIControl
	void SetUIControl(UIControl * pUIControl)
	{
		m_pUIControl = pUIControl;
	}
	
	//獲取UIControl
	UIControl * GetUIControl()
	{
		return m_pUIControl;
	}
};
具體3個控制元件類(也就是具體同事類)宣告如下:
//文字框
class EditText : public UIWindow
{
public:
	//文字框內容改變事件
    void OnChangeText();
};



//ListCtrl類
class ListCtrl : public UIWindow
{
public:
	//ListCtrl控制元件單擊事件
	void OnListCtrlClick();

	//重新整理圖片列表
	void UpdateImage();
};




//ListViw類
class ListView : public UIWindow
{
public:
	//ListViw控制元件雙擊事件
	void OnListViewDoubleClick();

	//重新整理資料夾列表
	void UpdateFolder();

	//選中資料夾
	void SelectFolder();

};
具體3個控制元件類(也就是具體同事類)宣告如下:
#include "UIControl.h"

/************************EditText********************/

//文字框內容改變事件
void EditText::OnChangeText()
{	
	UIControl * pUIControl = GetUIControl();
	
	pUIControl->OnChangeText();
}


/************************ListCtrl********************/

//ListCtrl控制元件單擊事件
void ListCtrl::OnListCtrlClick()
{
	UIControl * pUIControl = GetUIControl();
	
	pUIControl->OnListCtrlClick();
}



//重新整理圖片列表
void ListCtrl::UpdateImage()
{
	cout << "重新整理影象列表" << endl;
}


/************************ListView********************/

//ListViw控制元件雙擊事件
void ListView::OnListViewDoubleClick()
{
	UIControl * pUIControl = GetUIControl();
	
	pUIControl->OnListViewDoubleClick();
}



//重新整理資料夾列表
void ListView::UpdateFolder()
{
	cout << "重新整理檔案列表" << endl;
}



//選中資料夾
void ListView::SelectFolder()
{
	cout << "選中資料夾" << endl;
}

測試檔案實現程式碼如下:
#include <iostream>
#include "UIControl.h"
using namespace std;


int main()
{
	//建立UI控制器(中介者)
    UIControl * pUIControl = new UIControl();
	
	//建立文字框、ListCtrl、ListView控制元件(同事類)
	EditText * pEditText = new EditText();
	ListCtrl * pListCtrl = new ListCtrl();
	ListView * pListView = new ListView(); 
	
	//各個控制元件持有中介者的引用
	pEditText->SetUIControl(pUIControl);
	pListCtrl->SetUIControl(pUIControl);
	pListView->SetUIControl(pUIControl);
	
	//中介者認識所有控制元件
	pUIControl->SetEditText(pEditText);
	pUIControl->SetListCtrl(pListCtrl);
	pUIControl->SetListView(pListView);

	cout << "---------系統工作目錄改變------------" << endl;
	pEditText->OnChangeText();

	cout << endl << "---------ListView被雙擊------------" << endl;
	pListView->OnListViewDoubleClick();

	cout << endl << "---------ListCtrl被單擊------------" << endl;
	pListCtrl->OnListCtrlClick();

	cout << endl;

	//銷燬操作
	delete pUIControl;
	pUIControl = NULL;
	delete pEditText;
	pEditText = NULL;
	delete pListCtrl;
	pListCtrl = NULL;
	delete pListView;
	pListView = NULL;

	return 0;
}

編譯並執行,結果如下:
    如果需要引入新的具體同事類,只需要繼承抽象同事類並實現其中的方法即可,由於具體同事類之間並無直接的引用關係,因此原有所有同事類無須進行任何修改,它們與新增同事物件之間的互動可以通過修改或者增加具體中介者類來實現;如果需要在原有系統中增加新的具體中介者類,只需要繼承抽象中介者類(或已有的具體中介者類)並覆蓋其中定義的方法即可,在新的具體中介者中可以通過不同的方式來處理物件之間的互動,也可以增加對新增同事的引用和呼叫。在客戶端中只需要修改少許程式碼就可以實現中介者的更換。

3、中介者模式總結

    中介者模式將一個網狀的系統結構變成一個以中介者物件為中心的星形結構,在這個星型結構中,使用中介者物件與其他物件的一對多關係來取代原有物件之間的多對多關係。我們可以將物件之間的一些互動行為從各個物件中分離出來,並集中封裝在一箇中介者物件中,體現封裝變化原則。 各個同事類只需認識中介者類就可以了,無需認識其它同事類,解除了同事類之間的耦合,符合"迪迷特法則"。新增新的同事類,只需要繼承於抽象同事類就可以了,符合"開放封閉原則"。引入中介者模式,可維護性,可複用性,可拓展性提到增強。

1.主要優點

    中介者模式的主要優點如下:

    (1) 中介者模式簡化了物件之間的互動,它用中介者和同事的一對多互動代替了原來同事之間的多對多互動,一對多關係更容易理解、維護和擴充套件,將原本難以理解的網狀結構轉換成相對簡單的星型結構。

    (2) 中介者模式可將各同事物件解耦。中介者有利於各同事之間的鬆耦合,我們可以獨立的改變和複用每一個同事和中介者,增加新的中介者和新的同事類都比較方便,更好地符合“開閉原則”。

    (3) 可以減少子類生成,中介者將原本分佈於多個物件間的行為集中在一起,改變這些行為只需生成新的中介者子類即可,這使各個同事類可被重用,無須對同事類進行擴充套件。

    (4)各個同事類只需認識中介者類就可以了,無需認識其它同事類,解除了同事類之間的耦合,符合"迪迷特法則"。

    (5)我們可以將物件之間的一些互動行為從各個物件中分離出來,並集中封裝在一箇中介者物件中,體現封裝變化原則。

2.主要缺點

    在具體中介者類中包含了大量同事之間的互動細節,可能會導致具體中介者類非常複雜,使得系統難以維護,如果中介者出了問題,整個系統就無法運轉了,物件之間無法正常交流。中介者模式很容易在系統中應用,也很容易在系統中誤用。當系統出現了多對多互動複雜的物件群時,不要急於使用中介者模式,而要先反思你的系統在設計上是不是合理。

3.適用場景

    在以下情況下可以考慮使用中介者模式:

    (1) 系統中物件之間存在複雜的引用關係,系統結構混亂且難以理解。

    (2) 一個物件由於引用了其他很多物件並且直接和這些物件通訊,導致難以複用該物件

    (3) 想通過一箇中間類來封裝多個類中的行為,而又不想生成太多的子類。可以通過引入中介者類來實現,在中介者中定義物件互動的公共行為,如果需要改變行為則可以增加新的具體中介者類。

4.中介者模式具體應用

    (1)進銷存系統開發: 採購管理、銷售管理、庫存管理、供應商管理等,各個模組之間互相交流,存在多對多關心,可以使用一箇中介者來封裝這些模組之間的互動行為。

    (2)機場排程系統的開發,這個排程中心就相當於中介者,用於管理各飛機之間的正常通行。

    (3)QQ聊天中的群,群相當於中介者,處理各個物件之間的交流。

    (4)在GUI影象介面開發中,存在一個事件機制來處理各個UI控制元件之間的交流,這個事件機制就相當於中介者。

    (5)網路拓撲結構中的星行結構,所有計算機和交換機打交道,由互動機來負責計算機之間的正常通訊。

    (6)郵箱原理: 當給好友傳送一份郵件時候,該郵件先到達郵件伺服器(中轉站),由中轉轉負責把郵件發給好友。

    (7)生活中的中介: 電腦主機板控制CPU,硬碟,記憶體,顯示卡等;百合網相當於婚姻的中介;房屋中介