《二十三種設計模式》 第三篇 “中介者模式” (C++實現)

定義:

用一箇中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。

簡單點來說,將原來兩個直接引用或者依賴的對象拆開,在中間加入一個‘中介’對象,使得兩頭的對象分別和‘中介’對象引用或者依賴。

中介者模式的組成部分:

種類 說明
抽象中介者模式 抽象中介者角色定義統一的接口用於各同事角色之間的通信;
具體中介者角色 具體中介者角色通過協調各同事角色實現協作行爲。爲此它要知道並引用各個同事角色;
同事角色 每一個同事角角色都知道對應的具體中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協作。

中介者模式的特點:

MVC模式也算是中介者模式在框架設計中的一個應用,那麼MVC的控制層便是位於表現層與模型層之間的中介者。但是由於中介者模式在定義上比較鬆散,在結構上和觀察者模式、命令模式十分相像;而應用目的又與結構模式“門面模式”有些相似,所以要注意區分它們之間的異同。

① 在結構上看,中介者模式與觀察者模式、命令模式都添加了中間對象,只是中介者去掉了後兩者在行爲上的方向。因此中介者的應用可以仿照後兩者的例子去寫。

但是觀察者模式、命令模式中的觀察者、命令都是被客戶所知的,具體哪個觀察者、命令的應用都是由客戶來指定的;而大多中介者角色對於客戶程序卻是透明的。

② 從目的上看,中介者模式與觀察者模式、命令模式沒有任何關係,倒是與前面講過的門面模式有些相似。但是門面模式是介於客戶程序與子系統之間的,而中介者模式是介於子系統與子系統之間的。

門面模式與中介者模式的區別:

門面模式是將原有的複雜邏輯提取到一個統一的接口,簡化客戶對邏輯的使用。它是被客戶所感知的,而原有的複雜邏輯則被隱藏了起來。而中介者模式的加入並沒有改變客戶原有的使用習慣,它是隱藏在原有邏輯後面的,使得代碼邏輯更加清晰可用。

中介者模式的優點:

  1. 使用中介者模式最大的好處就是將同事角色解耦。這帶來了一系列的系統結構改善:提高了原有系統的可讀性、簡化原有系統的通信協議——將原有的多對多變爲一對多、提高了代碼的可複用性等等。
  2. 中介者角色集中了太多的責任,所有有關的同事對象都要由它來控制,所以不注意控制代碼的規模極易引發其他問題。


通過以上的說明不知道有沒有聽懂呢?不懂沒關係,下面我再通俗的講一遍。

中介者模式實際上就是一箇中介人,以我們生活上爲例,房產中介,租房中介,招聘中介等等,兩邊的人通過中介去聯繫,獲取對方所需要的,這就是中介者模式。

中介者模式一般用於後端與後端的聯繫,子系統與子系統的聯繫。而不能用於前後與後端(外觀模式用於前端與後端的聯繫)。

那爲什麼要使用中介者模式呢?

比如我們要去買房,我們幹嘛不直接去找銷售方去購買,而要找到中介,還被賺取中間商差價呢?
如下圖:
在這裏插入圖片描述
買家和買家直接進行交際,這樣不就更加快捷嗎?

事實是這樣,但是!

大家有沒有看到圖片中,五花八亂,如果不是我用不同的顏色畫出來,根本分不清楚誰和誰了,而且,這樣着做,還會有牽一髮而動全身的風險(例如:其中一個買家或者賣家出了問題,那麼就會牽涉到所有人)!不利於後期維護(緊耦合)。

那麼,如果我們在中間加上一個中介者會怎麼樣呢?

在這裏插入圖片描述

大家看到沒,是不是簡明多了,誰也不會牽涉到誰了,即使有其中一方出了問題,完全不需要動其他方面的代碼,只需要修改一下中介者中得代碼就行了,是不是效率就高很多了(鬆耦合)!
這就是中介者模式的奧妙!!!



不知道上面說的看懂了沒有呢?不懂得可以回去看多幾遍,重在理解!

下面我們以微信作爲中介者爲例子,以代碼方式在講解一遍,相信大家會搞懂的!

以故事引入主題:
話說盤古開天闢地後,誕生了世間萬物,其中就有神仙和凡人,還有微信。一天,織女下凡玩耍,被正在耕田的牛郎看見了,牛郎那是一見鍾情呀。於是呼,牛郎費盡心機,終於追到了織女,過上了“你來耕田我織衣”的生活!但是,好景不長,被岳母王母娘娘知道了,於是牛郎和織女便強行分離了。牛郎見不到織女了,終日以淚洗臉,但就在這時,我們的主角“微信”登場了!牛郎雖然無法直接與織女見面,但是他們可以使用微信的視頻聊天見面呀。日復一日,年復一年,他們都是隻能以微信視頻聊天見面,就連喜鵲都懶得去搭橋了。最終,還是感動了王母娘娘,准許了他們相見。全劇終!
(以上故事純屬瞎扯,如有雷同純屬巧合)

我們通過故事可以知道,牛郎和織女本來是生活交織在一起的,但是,由於“緊耦性”太強,出現了許多問題,最終被岳母分開了。所以爲了還能見面,他們使用了“中介”微信視頻通話進行見面。

看到了沒有,這就是中介者模式的運用!!!

要注重以“自然語言去理解”!

在寫代碼之前,先看一下中介者模式的UML類圖吧!
UML中介者模式類圖

其中中介和人類都是抽象類,而微信和牛郎和織女都是具體類!

好了明白了這些,讓我們看代碼吧:

爲了使代碼更加分明,我們把所有類都分開文件來寫:

ZhongJieZhe.h - 抽象類

#pragma once

class DanShen;	// 類聲明

// 抽象類
class ZhongJieZhe {
public:
	virtual void Send(const char* m_name, DanShen *danShen) = 0;	// 轉發消息
};

BG_ZhongJie.h - 具體類 中介(微信)

#pragma once

#include "ZhongJieZhe.h"
#include "DanShenBoy.h"
#include "DanShenGirl.h"

class DanShen;	// 類聲明

// 具體類----------------->繼承與抽象類
class BG_ZhongJie : public ZhongJieZhe {
public:
	BG_ZhongJie() : boy(NULL), girl(NULL) { }	// 默認初始化值爲NULL

	// 中介(微信)轉發消息
	void Send(const char *message, DanShen *danShen);

	void setBoy(DanShen *danShen);	// 在中介中註冊
	void setGirl(DanShen *danShen); // 在中介中註冊

private:
	// 中介(微信)需要知道所有的客戶
	DanShenBoy *boy;
	DanShenGirl *girl;
};

BG_ZhongJie.cpp - “微信”方法的具體實現

#include <iostream>
#include "BG_ZhongJie.h"


void BG_ZhongJie::Send(const char *message, DanShen *danShen) {
	
	// 判斷合法性
	if (!message || !danShen) {
		exit(-1);
	}

	// 判斷是誰發來的消息,進而對應轉發
	if (danShen == boy) {
		cout << "中介收到來自 單身男 的消息:【" << message << "】,準備發給 單身女~" << endl;
		girl->Notify(message);
	} else {
		cout << "中介收到來自 單身女 的消息:【" << message << "】,準備發給 單身男~" << endl;
		boy->Notify(message);
	}
}



// 註冊
void BG_ZhongJie::setBoy(DanShen *danShen) {
	boy = dynamic_cast<DanShenBoy*>(danShen);	// dynamic_cast 將父類對象轉換爲子類對象
}


// 註冊
void BG_ZhongJie::setGirl(DanShen *danShen) {
	girl = dynamic_cast<DanShenGirl*>(danShen);	// dynamic_cast 將父類對象轉換爲子類對象
}

DanShen.h - 抽象類(人類)

#pragma once

#include <iostream>
#include <string>
#include "ZhongJieZhe.h"


using namespace std;

// 抽象類
class DanShen {
public:											// 一種賦值類成員變量的方式
	DanShen(ZhongJieZhe *zhongJie, string name) : zhongJie(zhongJie), name(name) { }

	virtual void Send(const char *message) = 0;

protected:
	ZhongJieZhe *zhongJie;	// 中介
	string name;			// 客戶的姓名
};

DanShenBoy.h - 牛郎

#pragma once

#include "DanShen.h"

// 具體類
class DanShenBoy : public DanShen {
public:											// 一種賦值類成員變量的方式
	DanShenBoy(ZhongJieZhe * zhongJie, string name) : DanShen(zhongJie, name) { }

	// 發送消息給中介
	void Send(const char *message);

	// 收到中介發來的消息
	void Notify(const char *message);
};

DanShenBoy.cpp - 牛郎具體方法實現

#include "DanShenBoy.h"

// 把信息發送給中介
void DanShenBoy::Send(const char *message) {
	zhongJie->Send(message, this);
}


// 把收到的消息顯示出來
void DanShenBoy::Notify(const char *message) {
	cout << name << "收到消息:" << message << endl;
}

DanShenGirl.h - 織女

#pragma once

#include "DanShen.h"

// 具體類
class DanShenGirl : public DanShen {
public:												// 一種賦值類成員變量的方式
	DanShenGirl(ZhongJieZhe * zhongJie, string name) : DanShen(zhongJie, name) { }

	// 發送消息給中介
	void Send(const char *message);

	// 收到中介發來的消息
	void Notify(const char *message);
};

DanShenGirl.cpp - 織女具體方法實現

#include "DanShenGirl.h"

// 把信息發送給中介
void DanShenGirl::Send(const char *message) {
	zhongJie->Send(message, this);
}


// 把收到的消息顯示出來
void DanShenGirl::Notify(const char *message) {
	cout << name << "收到消息:" << message << endl;
}

main.cpp - 主方法

#include "BG_ZhongJie.h"

int main(void) {
	BG_ZhongJie*zj = new BG_ZhongJie();					// 定義中介
	DanShen *niuLang = new DanShenBoy(zj, "牛郎");		// 定義牛郎
	DanShen *zhiNv = new DanShenGirl(zj, "織女");		// 定義織女

	// 牛郎和織女在中介中註冊
	zj->setBoy(niuLang);
	zj->setGirl(zhiNv);

	// 牛郎發送消息給中介
	niuLang->Send("今晚老地方見!");

	cout << endl;

	// 織女發送消息給中介
	zhiNv->Send("好,準時到!");

	system("pause");
	return 0;
}

運行截圖:
在這裏插入圖片描述

代碼中都有註釋,相信也會能看得懂!
不懂的多看幾次!


總結:

注重理解中介者模式的思想(通過我上面舉的例子),再來研究代碼!

記得從“自然語言”去理解!

上面代碼中提到的“註冊”的意思是:得先告訴中介你是誰,中介才能幫你轉發!
差不多這樣的意思!

如果有一個類發生了改變,不需要變動其他地方,只需要改動中介裏的Send方法就行了!

void BG_ZhongJie::Send(const char *message, DanShen *danShen) {
	
	// 判斷合法性
	if (!message || !danShen) {
		exit(-1);
	}

	// 判斷是誰發來的消息,進而對應轉發
	if (danShen == boy) {
		cout << "中介收到來自 單身男 的消息:【" << message << "】,準備發給 單身女~" << endl;
		girl->Notify(message);
	} else {
		cout << "中介收到來自 單身女 的消息:【" << message << "】,準備發給 單身男~" << endl;
		boy->Notify(message);
	}
}

中介者模式的精華之處也在這裏是現實了!

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