定義:
用一箇中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。
簡單點來說,將原來兩個直接引用或者依賴的對象拆開,在中間加入一個‘中介’對象,使得兩頭的對象分別和‘中介’對象引用或者依賴。
中介者模式的組成部分:
種類 | 說明 |
---|---|
抽象中介者模式 | 抽象中介者角色定義統一的接口用於各同事角色之間的通信; |
具體中介者角色 | 具體中介者角色通過協調各同事角色實現協作行爲。爲此它要知道並引用各個同事角色; |
同事角色 | 每一個同事角角色都知道對應的具體中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協作。 |
中介者模式的特點:
MVC模式也算是中介者模式在框架設計中的一個應用,那麼MVC的控制層便是位於表現層與模型層之間的中介者。但是由於中介者模式在定義上比較鬆散,在結構上和觀察者模式、命令模式十分相像;而應用目的又與結構模式“門面模式”有些相似,所以要注意區分它們之間的異同。
① 在結構上看,中介者模式與觀察者模式、命令模式都添加了中間對象,只是中介者去掉了後兩者在行爲上的方向。因此中介者的應用可以仿照後兩者的例子去寫。
但是觀察者模式、命令模式中的觀察者、命令都是被客戶所知的,具體哪個觀察者、命令的應用都是由客戶來指定的;而大多中介者角色對於客戶程序卻是透明的。
② 從目的上看,中介者模式與觀察者模式、命令模式沒有任何關係,倒是與前面講過的門面模式有些相似。但是門面模式是介於客戶程序與子系統之間的,而中介者模式是介於子系統與子系統之間的。
門面模式與中介者模式的區別:
門面模式是將原有的複雜邏輯提取到一個統一的接口,簡化客戶對邏輯的使用。它是被客戶所感知的,而原有的複雜邏輯則被隱藏了起來。而中介者模式的加入並沒有改變客戶原有的使用習慣,它是隱藏在原有邏輯後面的,使得代碼邏輯更加清晰可用。
中介者模式的優點:
- 使用中介者模式最大的好處就是將同事角色解耦。這帶來了一系列的系統結構改善:提高了原有系統的可讀性、簡化原有系統的通信協議——將原有的多對多變爲一對多、提高了代碼的可複用性等等。
- 中介者角色集中了太多的責任,所有有關的同事對象都要由它來控制,所以不注意控制代碼的規模極易引發其他問題。
通過以上的說明不知道有沒有聽懂呢?不懂沒關係,下面我再通俗的講一遍。
中介者模式實際上就是一箇中介人,以我們生活上爲例,房產中介,租房中介,招聘中介等等,兩邊的人通過中介去聯繫,獲取對方所需要的,這就是中介者模式。
中介者模式一般用於後端與後端的聯繫,子系統與子系統的聯繫。而不能用於前後與後端(外觀模式用於前端與後端的聯繫)。
那爲什麼要使用中介者模式呢?
比如我們要去買房,我們幹嘛不直接去找銷售方去購買,而要找到中介,還被賺取中間商差價呢?
如下圖:
買家和買家直接進行交際,這樣不就更加快捷嗎?
事實是這樣,但是!
大家有沒有看到圖片中,五花八亂,如果不是我用不同的顏色畫出來,根本分不清楚誰和誰了,而且,這樣着做,還會有牽一髮而動全身的風險(例如:其中一個買家或者賣家出了問題,那麼就會牽涉到所有人)!不利於後期維護(緊耦合)。
那麼,如果我們在中間加上一個中介者會怎麼樣呢?
大家看到沒,是不是簡明多了,誰也不會牽涉到誰了,即使有其中一方出了問題,完全不需要動其他方面的代碼,只需要修改一下中介者中得代碼就行了,是不是效率就高很多了(鬆耦合)!
這就是中介者模式的奧妙!!!
不知道上面說的看懂了沒有呢?不懂得可以回去看多幾遍,重在理解!
下面我們以微信作爲中介者爲例子,以代碼方式在講解一遍,相信大家會搞懂的!
以故事引入主題:
話說盤古開天闢地後,誕生了世間萬物,其中就有神仙和凡人,還有微信。一天,織女下凡玩耍,被正在耕田的牛郎看見了,牛郎那是一見鍾情呀。於是呼,牛郎費盡心機,終於追到了織女,過上了“你來耕田我織衣”的生活!但是,好景不長,被岳母王母娘娘知道了,於是牛郎和織女便強行分離了。牛郎見不到織女了,終日以淚洗臉,但就在這時,我們的主角“微信”登場了!牛郎雖然無法直接與織女見面,但是他們可以使用微信的視頻聊天見面呀。日復一日,年復一年,他們都是隻能以微信視頻聊天見面,就連喜鵲都懶得去搭橋了。最終,還是感動了王母娘娘,准許了他們相見。全劇終!
(以上故事純屬瞎扯,如有雷同純屬巧合)
我們通過故事可以知道,牛郎和織女本來是生活交織在一起的,但是,由於“緊耦性”太強,出現了許多問題,最終被岳母分開了。所以爲了還能見面,他們使用了“中介”微信視頻通話進行見面。
看到了沒有,這就是中介者模式的運用!!!
要注重以“自然語言去理解”!
在寫代碼之前,先看一下中介者模式的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);
}
}
中介者模式的精華之處也在這裏是現實了!