定义:
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
简单点来说,将原来两个直接引用或者依赖的对象拆开,在中间加入一个‘中介’对象,使得两头的对象分别和‘中介’对象引用或者依赖。
中介者模式的组成部分:
种类 | 说明 |
---|---|
抽象中介者模式 | 抽象中介者角色定义统一的接口用于各同事角色之间的通信; |
具体中介者角色 | 具体中介者角色通过协调各同事角色实现协作行为。为此它要知道并引用各个同事角色; |
同事角色 | 每一个同事角角色都知道对应的具体中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。 |
中介者模式的特点:
MVC模式也算是中介者模式在框架设计中的一个应用,那么MVC的控制层便是位于表现层与模型层之间的中介者。但是由于中介者模式在定义上比较松散,在结构上和观察者模式、命令模式十分相像;而应用目的又与结构模式“门面模式”有些相似,所以要注意区分它们之间的异同。
① 在结构上看,中介者模式与观察者模式、命令模式都添加了中间对象,只是中介者去掉了后两者在行为上的方向。因此中介者的应用可以仿照后两者的例子去写。
但是观察者模式、命令模式中的观察者、命令都是被客户所知的,具体哪个观察者、命令的应用都是由客户来指定的;而大多中介者角色对于客户程序却是透明的。
② 从目的上看,中介者模式与观察者模式、命令模式没有任何关系,倒是与前面讲过的门面模式有些相似。但是门面模式是介于客户程序与子系统之间的,而中介者模式是介于子系统与子系统之间的。
门面模式与中介者模式的区别:
门面模式是将原有的复杂逻辑提取到一个统一的接口,简化客户对逻辑的使用。它是被客户所感知的,而原有的复杂逻辑则被隐藏了起来。而中介者模式的加入并没有改变客户原有的使用习惯,它是隐藏在原有逻辑后面的,使得代码逻辑更加清晰可用。
中介者模式的优点:
通过以上的说明不知道有没有听懂呢?不懂没关系,下面我再通俗的讲一遍。
中介者模式实际上就是一个中介人,以我们生活上为例,房产中介,租房中介,招聘中介等等,两边的人通过中介去联系,获取对方所需要的,这就是中介者模式。
中介者模式一般用于后端与后端的联系,子系统与子系统的联系。而不能用于前后与后端(外观模式用于前端与后端的联系)。
那为什么要使用中介者模式呢?
比如我们要去买房,我们干嘛不直接去找销售方去购买,而要找到中介,还被赚取中间商差价呢?
如下图:
买家和买家直接进行交际,这样不就更加快捷吗?
事实是这样,但是!
大家有没有看到图片中,五花八乱,如果不是我用不同的颜色画出来,根本分不清楚谁和谁了,而且,这样着做,还会有牵一发而动全身的风险(例如:其中一个买家或者卖家出了问题,那么就会牵涉到所有人)!不利于后期维护(紧耦合)。
那么,如果我们在中间加上一个中介者会怎么样呢?
大家看到没,是不是简明多了,谁也不会牵涉到谁了,即使有其中一方出了问题,完全不需要动其他方面的代码,只需要修改一下中介者中得代码就行了,是不是效率就高很多了(松耦合)!
这就是中介者模式的奥妙!!!
不知道上面说的看懂了没有呢?不懂得可以回去看多几遍,重在理解!
下面我们以微信作为中介者为例子,以代码方式在讲解一遍,相信大家会搞懂的!
以故事引入主题:
话说盘古开天辟地后,诞生了世间万物,其中就有神仙和凡人,还有微信。一天,织女下凡玩耍,被正在耕田的牛郎看见了,牛郎那是一见钟情呀。于是呼,牛郎费尽心机,终于追到了织女,过上了“你来耕田我织衣”的生活!但是,好景不长,被岳母王母娘娘知道了,于是牛郎和织女便强行分离了。牛郎见不到织女了,终日以泪洗脸,但就在这时,我们的主角“微信”登场了!牛郎虽然无法直接与织女见面,但是他们可以使用微信的视频聊天见面呀。日复一日,年复一年,他们都是只能以微信视频聊天见面,就连喜鹊都懒得去搭桥了。最终,还是感动了王母娘娘,准许了他们相见。全剧终!
(以上故事纯属瞎扯,如有雷同纯属巧合)
我们通过故事可以知道,牛郎和织女本来是生活交织在一起的,但是,由于“紧耦性”太强,出现了许多问题,最终被岳母分开了。所以为了还能见面,他们使用了“中介”微信视频通话进行见面。
看到了没有,这就是中介者模式的运用!!!
要注重以“自然语言去理解”!
其中中介和人类都是抽象类,而微信和牛郎和织女都是具体类!
好了明白了这些,让我们看代码吧:
为了使代码更加分明,我们把所有类都分开文件来写:
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
#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
#include
#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);
}
}
中介者模式的精华之处也在这里是现实了!