设计模式之中介模式

通俗地讲,**中介者模式(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 的地方可能都要重新编译) (通常只需重新编译中介者)
相关推荐
驴儿响叮当20105 小时前
设计模式之命令模式
设计模式·命令模式
驴儿响叮当20107 小时前
设计模式之适配器模式
设计模式·适配器模式
HEU_firejef8 小时前
设计模式——代理模式
设计模式·代理模式
Coder_Boy_9 小时前
从单体并发工具类到分布式并发:思想演进与最佳实践(二)
java·spring boot·分布式·微服务·设计模式
geovindu18 小时前
python: Memento Pattern
开发语言·python·设计模式·备忘录模式
HEU_firejef1 天前
设计模式——单例模式
单例模式·设计模式
电子科技圈1 天前
SmartDV与Mirabilis Design宣布就SmartDV IP系统级模型达成战略合作
大数据·设计模式·软件工程
带娃的IT创业者1 天前
解密OpenClaw系列04-OpenClaw设计模式应用
设计模式·软件工程·软件架构·ai agent·ai智能体开发·openclaw
kong79069281 天前
设计模式-策略模式
设计模式·策略模式·行为设计模式