通俗地讲,**中介者模式(Mediator Pattern)**就是给一群乱成一团、互相指手画脚的对象找了一个"大管家"或者"协调员"。
如果没有这个管家,大家(对象)之间需要互相打招呼,关系网会像乱麻一样;有了管家,大家只需要跟管家说话,剩下的琐事由管家去转达。
想象一下一个商场有:空调、照明灯、烟雾报警器、自动喷淋头。
场景对比:乱套 vs 有序
❌ 如果没有中介者(网状关系)
-
烟雾报警器 发现火情,它得自己去给空调 发指令(关闭送风),去给照明灯 发指令(切换应急灯),还要去给喷淋头发指令(喷水)。
-
后果: 报警器必须知道空调、灯、喷淋头的所有接口。如果商场新加了一个"自动报警电话",你还得改报警器的代码。这叫高度耦合。
✅ 如果有中介者(星形关系)
-
所有设备只连接到**"物业管理系统"**。
-
报警器喊一声:"着火了!"。
-
**物业管理系统(中介者)**收到后,按照逻辑去关闭空调、打开喷淋、切换灯光。
-
后果: 报警器只管报警,至于谁去救火、谁去关风,它一概不操心。
老规矩,用代码解释两者区别:
1.无中介者模式
#include <iostream>
// 窗帘
class Curtain {
public:
void open() { std::cout << "窗帘打开了。\n"; }
};
// 咖啡机
class CoffeeMachine {
public:
void start() { std::cout << "咖啡机开始煮咖啡。\n"; }
};
// 闹钟:它是整个逻辑的中心,必须持有其他两个对象的指针
class Alarm {
private:
Curtain* curtain;
CoffeeMachine* coffee;
public:
// 构造函数:你得亲手把窗帘和咖啡机塞给闹钟
Alarm(Curtain* c, CoffeeMachine* m) : curtain(c), coffee(m) {}
void ring() {
std::cout << "闹钟响了!\n";
curtain->open(); // 闹钟直接指挥窗帘
coffee->start(); // 闹钟直接指挥咖啡机
}
};
int main() {
Curtain* c = new Curtain();
CoffeeMachine* m = new CoffeeMachine();
// 这种初始化方式叫"强耦合":没有c和m,alarm连实例都创不出来
Alarm* alarm = new Alarm(c, m);
alarm->ring();
delete c; delete m; delete alarm;
return 0;
}
2.有中介者模式
#include <iostream>
class Mediator; // 前向声明
// 抽象设备(所有设备只和中介者打交道)
class Device {
protected:
Mediator* mediator;
public:
Device(Mediator* m) : mediator(m) {}
};
// 抽象中介者接口
class Mediator {
public:
virtual void changed(Device* sender) = 0;
};
// --- 具体设备 ---
class Alarm : public Device {
public:
using Device::Device;
void ring() {
std::cout << "闹钟响了!\n";
mediator->changed(this); // 闹钟只负责报告:我响了,至于谁跟着动,它不操心
}
};
class Curtain : public Device {
public:
using Device::Device;
void open() { std::cout << "窗帘打开了。\n"; }
};
class CoffeeMachine : public Device {
public:
using Device::Device;
void start() { std::cout << "咖啡机开始煮咖啡。\n"; }
};
// --- 具体中介者:智能大脑 ---
class SmartHomeMediator : public Mediator {
public:
Alarm* alarm;
Curtain* curtain;
CoffeeMachine* coffee;
// 中介者接收信号,决定如何联动
void changed(Device* sender) override {
if (sender == alarm) {
curtain->open();
coffee->start();
}
}
};
int main() {
// 1. 先把大脑(中介者)创造出来
SmartHomeMediator* mediator = new SmartHomeMediator();
// 2. 创建设备时,只需要告诉它们大脑是谁就行
Alarm* alarm = new Alarm(mediator);
Curtain* curtain = new Curtain(mediator);
CoffeeMachine* coffee = new CoffeeMachine(mediator);
// 3. 大脑需要知道它管哪些设备
mediator->alarm = alarm;
mediator->curtain = curtain;
mediator->coffee = coffee;
// 4. 触发动作
alarm->ring();
delete mediator; delete alarm; delete curtain; delete coffee;
return 0;
}
3. 这两个代码最核心的区别
如果你看 main 函数,可能觉得中介者模式更麻烦了。但请看这两个类的成员变量:
-
无中介者的
Alarm: 里面存了Curtain*和CoffeeMachine*。这意味着:
Alarm的代码里必须#include "Curtain.h"和#include "CoffeeMachine.h"。它被这两个类紧紧套牢了。 -
有中介者的
Alarm: 里面只有一个Mediator*。这意味着:
Alarm不需要知道窗帘是什么,也不需要知道咖啡机是什么。它只知道"当我有事时,找中介者汇报"。
中介者模式的精髓不是为了减少代码量,而是为了"划清界限"。
-
没有中介者:
Alarm是主控器(又当闹钟又当管家)。 -
有了中介者:
Alarm只是一个纯粹的闹钟。
4.场景:闹钟响了 --》窗帘开、咖啡机动、新增:扫地机扫地
<1> 无中介者模式:牵一发而动全身
在没有中介者时,Alarm 类就像一个"管家婆",它必须认识所有人。增加扫地机,你得去拆开 Alarm 类的代码进行大手术。
// --- 1. 新增一个扫地机类 ---
class Robot {
public:
void clean() { std::cout << "【扫地机】开始清扫地板...\n"; }
};
// --- 2. 必须修改原有的 Alarm 类(大手术!) ---
class Alarm {
Curtain* pCurtain;
CoffeeMachine* pCM;
Robot* pRobot; // 【修改点1】:增加新成员
public:
// 【修改点2】:修改构造函数,强迫闹钟必须认识机器人
Alarm(Curtain* c, CoffeeMachine* m, Robot* r) : pCurtain(c), pCM(m), pRobot(r) {}
void ring() {
std::cout << "闹钟响了!\n";
pCurtain->open();
pCM->start();
pRobot->clean(); // 【修改点3】:修改业务逻辑
}
};
// --- 3. Main 函数也得改 ---
int main() {
Curtain* c = new Curtain();
CoffeeMachine* m = new CoffeeMachine();
Robot* r = new Robot(); // 新增对象
Alarm* alarm = new Alarm(c, m, r); // 这里的传参变了
alarm->ring();
return 0;
}
闹钟类非常"脆弱"。每次家里买新家电,你都得把闹钟拆开接几根线,这在大型工程里叫**"代码污染"**。
<2> 中介者模式:优雅的"插拔式"扩展
在有中介者时,Alarm 类一行代码都不用改。它只负责喊一声"我响了",剩下的交给"大脑"去协调。
// --- 1. 新增扫地机类(继承自Device) ---
class Robot : public Device {
public:
using Device::Device;
void clean() { std::cout << "【扫地机】开始清扫地板...\n"; }
};
// --- 2. 修改中介者(大脑)的逻辑即可 ---
class SmartHomeMediator : public Mediator {
Alarm* alarm;
Curtain* curtain;
CoffeeMachine* coffee;
Robot* robot; // 新增机器人引用
public:
void setDevices(Alarm* a, Curtain* c, CoffeeMachine* cm, Robot* r) {
alarm = a; curtain = c; coffee = cm; robot = r;
}
void changed(Device* d) override {
if (d == alarm) { // 闹钟响了,大脑下令:
curtain->open();
coffee->start();
robot->clean(); // 【唯一修改点】:在这里加一行逻辑
}
}
};
// --- 3. Main 函数逻辑清爽 ---
int main() {
SmartHomeMediator* mediator = new SmartHomeMediator();
// 所有设备初始化方式一模一样,只认中介者
Alarm* alarm = new Alarm(mediator);
Curtain* curtain = new Curtain(mediator);
CoffeeMachine* coffee = new CoffeeMachine(mediator);
Robot* robot = new Robot(mediator); // 新设备轻松接入
mediator->setDevices(alarm, curtain, coffee, robot);
// 闹钟触发,闹钟的代码完全没动过!
alarm->ring();
return 0;
}
关键区别在哪?
| 比较项 | 无中介者(硬编码) | 中介者模式 |
|---|---|---|
| 闹钟类的代码 | 必须修改(增加指针、改构造、改逻辑)。 | 完全不动(它甚至不知道机器人的存在)。 |
| 类的关系 | 闹钟关联了:窗帘、咖啡机、机器人。 | 闹钟只关联了:中介者。 |
| 谁更累? | 闹钟很累,它得管所有人的死活。 | 中介者很累,但它集中处理了所有麻烦。 |
-
不用中介者: 你的代码像毛线团,拽一根线,整团都要跟着动。
-
用中介者: 你的代码像插线板。想加个电器?插上去,然后在插线板的控制开关(中介者)里写好逻辑就行了。
这就叫"解耦"。下次如果你发现一个类里的指针越来越多,多到你数不过来的时候,就是该请"中介者"出场的时候了!
5.疑问
中介者模式并没有让代码总量变少,甚至"中介者"类自己改得比谁都多???
我们可以从以下三个维度看清"改中介者"和"改闹钟"的本质区别:
(1) "稳定的心"与"善变的大脑"
在软件开发中,我们希望**底层的执行类(闹钟、灯、机器人)**越纯粹越好,不要因为业务逻辑变了就去动它们。
-
如果不请中介者:
闹钟本来的职责是"计时"和"发声"。但为了联动,你给它塞了机器人、咖啡机。这时候,闹钟就不再是一个纯粹的闹钟了,它变成了一个"家电指挥部"。
后果: 如果你要把这个闹钟代码拿到另一个没有机器人的项目里用,代码会报错,因为它里面写死了机器人。
-
如果请了中介者:
闹钟只管"响",它不认识任何人。
优点: 闹钟类达到了**"100%可复用"**。它可以在任何地方工作,因为它只依赖一个抽象的中介者接口。
(2) "改一处"与"改一堆"的风险对比
你觉得中介者改了很多,是因为在这个小例子里只有 3 个设备。想象一下如果你有 20 个设备,且它们之间有复杂的互相影响(比如:开 A 必须关 B,开 C 必须检查 D 是否关闭):
-
不用中介者:
你可能需要修改 10 个类的代码,每个类都要加逻辑、加成员变量。改的地方越多,写出 BUG 的概率是指数级上升的。(你可能会忘了改其中一个,导致系统崩溃)。
-
用中介者:
所有的逻辑乱如麻,但乱得整齐------全都在中介者一个类里。
好处: 你只要盯着中介者这一个文件改就行了。测试的时候,你也只需要重点测试中介者逻辑。
(3) 程序员的"心智负担"
-
乱战模式(无中介者): 当你想看"扫地机"什么时候动,你得去翻闹钟的代码、翻传感器的代码、翻手机 APP 的代码......因为谁都可能指挥它。
-
上帝模式(中介者): 当你想看"扫地机"什么时候动,你只需要打开
SmartHomeMediator.cpp。这个文件就是整个家的**"逻辑说明书"**。
(4) 数据对比
| 修改维度 | 修改 Alarm 类 (无中介者) | 修改 Mediator 类 (有中介者) |
|---|---|---|
| 影响范围 | 高(Alarm 类本身被破坏,无法独立使用) | 低(Alarm 类完全没变,依然纯洁) |
| 逻辑清晰度 | 差(逻辑散落在各个角落) | 好(逻辑集中在"大脑"里) |
| 编译成本 | 高(所有引用 Alarm 的地方可能都要重新编译) | 低(通常只需重新编译中介者) |