中介者模式:告别组件乱炖!让代码协作优雅如 “外交官”

📕目录

前言

一、从 “租房乱象” 说起:为什么需要中介者模式?

1.1 无中介者模式的代码痛点:以智能家居为例

无中介者的错误示范代码

这段代码的致命问题

二、中介者模式核心解析:4 个角色 + 1 个核心思想

2.1 中介者模式的 4 个核心角色

1. 抽象中介者(Mediator):交互规则的 “协议”

2. 具体中介者(Concrete Mediator):实际协调的 “外交官”

3. 抽象同事类(Colleague):组件的 “通用模板”

4. 具体同事类(Concrete Colleague):实际工作的 “组件”

2.2 中介者模式的核心思想:“统一协调,隔离依赖”

三、C++ 实战:用中介者模式重构智能家居系统

3.1 第一步:定义抽象同事类(Colleague)

3.2 第二步:定义抽象中介者(Mediator)

3.3 第三步:实现具体同事类(Concrete Colleague)

1. 灯组件(Light)

2. 空调组件(AirConditioner)

3. 窗帘组件(Curtain)

4. 音响组件(Speaker)

3.4 第四步:实现具体中介者(Concrete Mediator)

3.5 第五步:测试中介者模式的效果

3.6 测试结果与代码分析

四、中介者模式的进阶技巧:避坑指南 + 实际应用场景

4.1 中介者模式的常见 “坑” 及避坑指南

坑 1:中介者变成 “上帝类”

坑 2:同事类依赖具体中介者,而非抽象中介者

坑 3:滥用中介者模式

坑 4:忽略中介者的线程安全

4.2 中介者模式的实际应用场景

1. GUI 组件交互(桌面应用)

2. 分布式系统的协调中心(后端开发)

3. 游戏中的场景管理器(游戏开发)

4. 消息队列的生产者 - 消费者模型(中间件)

5. 团队协作系统(办公软件)

4.3 中介者模式与观察者模式的区别

五、总结:中介者模式的核心价值


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

前言

你是否遇到过这样的窘境:一个系统里多个组件相互依赖,A 组件要调用 B,B 要联动 C,C 又要通知 A,最后形成一张密密麻麻的 “依赖网”?就像一群人各说各的,没有统一协调,沟通混乱又低效。比如智能家居里的灯、空调、窗帘、音响,要是它们各自直接通信,想实现 “回家模式”“睡眠模式”,代码只会变成一团乱麻 —— 改一个设备的逻辑,牵一发而动全身。

今天咱们要聊的中介者模式,就是解决这类 “组件协同混乱” 问题的 “外交官”。它能让多个相互依赖的组件通过一个统一的中介者沟通,组件之间不再直接关联,从而彻底解开耦合。本文会从生活场景切入,带你吃透中介者模式的核心思想,再通过完整的 C++ 实战案例手把手教你落地,全是能直接复用的干货,新手也能轻松看懂。

一、从 “租房乱象” 说起:为什么需要中介者模式?

在讲中介者模式的定义之前,咱们先从一个你可能经历过的场景 —— 租房,聊聊 “无中介” 的痛点。假设没有房产中介,租客要找房东,房东要找租客,过程会有多麻烦:

  • 租客得挨个小区打听,联系每个房东问房源,重复说明自己的需求(预算、户型、租期);
  • 房东得接待一波又一波租客看房,重复介绍房子情况,还要处理议价、签约、维修等琐事;
  • 一旦租客换房、房东涨价,双方得重新对接,中间任何环节出问题,都要直接沟通扯皮。

这就像编程里的 “组件直接通信”:每个组件(租客、房东)都要和其他组件建立直接联系,组件越多,依赖关系越复杂,系统维护成本越高。

1.1 无中介者模式的代码痛点:以智能家居为例

咱们用 C++ 写一个简单的智能家居系统,看看没有中介者时,多个设备协同的代码会有多糟糕。需求很简单:实现 “回家模式”(灯开、空调调 25℃、窗帘拉开、音响放音乐)和 “睡眠模式”(灯关、空调调 22℃、窗帘拉上、音响关)。

无中介者的错误示范代码
#include <iostream>
#include <string>
using namespace std;

// 灯组件
class Light {
public:
    void turnOn() { cout << "灯光已开启" << endl; }
    void turnOff() { cout << "灯光已关闭" << endl; }
};

// 空调组件
class AirConditioner {
public:
    void setTemperature(int temp) { 
        cout << "空调温度已调节至:" << temp << "℃" << endl; 
    }
};

// 窗帘组件
class Curtain {
public:
    void open() { cout << "窗帘已拉开" << endl; }
    void close() { cout << "窗帘已拉上" << endl; }
};

// 音响组件
class Speaker {
public:
    void playMusic() { cout << "音响正在播放舒缓音乐" << endl; }
    void stopMusic() { cout << "音响已关闭" << endl; }
};

// 客户端:直接协调所有设备(噩梦开始)
int main() {
    // 初始化所有设备
    Light light;
    AirConditioner ac;
    Curtain curtain;
    Speaker speaker;

    // 回家模式:客户端要手动调用每个设备的方法
    cout << "=== 执行回家模式 ===" << endl;
    light.turnOn();
    ac.setTemperature(25);
    curtain.open();
    speaker.playMusic();

    // 睡眠模式:客户端又要手动调用一遍
    cout << "\n=== 执行睡眠模式 ===" << endl;
    light.turnOff();
    ac.setTemperature(22);
    curtain.close();
    speaker.stopMusic();

    // 新增"离家模式":客户端还要再写一遍调用逻辑
    cout << "\n=== 执行离家模式 ===" << endl;
    light.turnOff();
    ac.setTemperature(28); // 离家时空调调至节能温度
    curtain.close();
    speaker.stopMusic();

    return 0;
}
这段代码的致命问题

你可能觉得这段代码能跑起来,但随着系统扩展,它的弊端会暴露无遗:

  1. 组件耦合严重:客户端要直接操作所有设备,设备越多,客户端的代码越臃肿。如果新增 “影院模式”(灯关、窗帘拉上、空调调 24℃、音响开最大),就得在客户端新增一堆设备调用代码;
  2. 职责混乱:“模式切换” 的逻辑本该是一个独立的功能,但现在散落在客户端,客户端变成了 “万能协调者”,违反了 “单一职责原则”;
  3. 维护成本极高:如果要修改某个模式的逻辑(比如回家模式的空调温度从 25℃改成 24℃),只能在客户端找到对应的代码修改;如果某个设备的方法名变了(比如playMusic改成startPlay),所有调用该方法的地方都要改;
  4. 扩展性差:新增设备(比如加湿器)时,要修改所有相关的模式逻辑,在每个模式里都添加加湿器的调用代码,完全违反 “开闭原则”。

而中介者模式的核心思路就是:引入一个 “中介者”,让所有组件都通过中介者沟通,组件之间不再直接关联,模式切换的逻辑也交给中介者统一管理。就像租房时找中介,租客不用直接对接房东,房东也不用接待所有租客,所有沟通和协调都通过中介完成,效率大幅提升。

二、中介者模式核心解析:4 个角色 + 1 个核心思想

中介者模式(Mediator Pattern)的定义是:定义一个对象,封装一系列对象的交互方式,使这些对象不需要显式地相互引用,从而使其耦合松散,并且可以独立地改变它们之间的交互。

简单说,中介者模式就是 “化零为整”—— 把组件之间的多对多交互,变成中介者与组件之间的一对多交互,让系统结构从 “网状” 变成 “星形”。

2.1 中介者模式的 4 个核心角色

咱们结合智能家居的例子,逐个拆解这 4 个角色,保证你一看就懂:

1. 抽象中介者(Mediator):交互规则的 “协议”

抽象中介者是所有具体中介者的父类,它定义了中介者应该具备的核心接口 —— 比如 “注册组件”“转发消息”。就像租房中介的行业规范,规定了中介必须能帮租客找房、帮房东挂房。

它的核心职责:

  • 提供注册同事组件的接口(让组件能加入中介者的管理);
  • 提供消息转发的接口(让组件之间通过中介者传递信息)。
2. 具体中介者(Concrete Mediator):实际协调的 “外交官”

具体中介者实现了抽象中介者的接口,是真正负责协调组件的核心。它会维护所有组件的引用,接收某个组件的消息后,根据消息类型协调其他组件做出响应。比如智能家居中介者,会维护灯、空调、窗帘、音响的引用,收到 “回家模式” 的消息后,统一调用各个设备的对应方法。

它的核心职责:

  • 管理所有同事组件(保存组件的引用);
  • 封装组件之间的交互逻辑(比如模式切换的规则);
  • 接收组件的消息,转发并协调其他组件响应。
3. 抽象同事类(Colleague):组件的 “通用模板”

抽象同事类是所有具体组件的父类,它定义了组件的通用接口(比如发送消息、接收消息),并且持有一个抽象中介者的引用 —— 组件通过这个引用和中介者通信,而不是直接和其他组件通信。

它的核心职责:

  • 定义组件的基本行为(发送消息、接收消息);
  • 持有中介者的引用,通过中介者与其他组件交互。
4. 具体同事类(Concrete Colleague):实际工作的 “组件”

具体同事类实现了抽象同事类的接口,是系统中真正执行具体功能的组件(比如灯、空调、窗帘)。它们不再直接和其他组件通信,而是通过中介者发送消息,收到中介者的通知后执行对应的操作。

它的核心职责:

  • 实现自身的业务逻辑(比如灯的开关、空调调温);
  • 发送消息时,通过中介者转发,而不是直接发给其他组件;
  • 接收中介者的消息,执行对应的响应操作。

2.2 中介者模式的核心思想:“统一协调,隔离依赖”

把 4 个角色串起来,中介者模式的工作流程可以总结为 5 步:

  1. 所有具体同事类(灯、空调等)在初始化时,会注册到具体中介者中;
  2. 客户端想要触发某个交互(比如执行回家模式),直接调用具体中介者的对应方法,或者让某个组件发送消息给中介者;
  3. 具体中介者收到消息后,根据预设的逻辑(比如回家模式的规则),向相关的同事类发送通知;
  4. 收到通知的同事类执行自身的业务逻辑;
  5. 整个过程中,同事类之间没有任何直接引用,所有交互都通过中介者完成。

用一张流程图展示 “回家模式” 的执行过程,更直观:

    A[客户端] -- 调用executeHomeMode() --> B[具体中介者(智能家居中介)]
    B -- 通知灯开启 --> C[具体同事类(灯)]
    B -- 通知空调调25℃ --> D[具体同事类(空调)]
    B -- 通知窗帘拉开 --> E[具体同事类(窗帘)]
    B -- 通知音响放音乐 --> F[具体同事类(音响)]
    C -- 执行turnOn() --> G[灯光开启]
    D -- 执行setTemperature(25) --> H[空调调至25℃]
    E -- 执行open() --> I[窗帘拉开]
    F -- 执行playMusic() --> J[音响放音乐]

通过这种方式,组件之间的耦合被彻底解开,模式切换的逻辑集中在中介者中,系统的扩展性和维护性大幅提升。

三、C++ 实战:用中介者模式重构智能家居系统

理论讲完了,咱们马上用 C++ 把智能家居系统重构一遍。按照中介者模式的 4 个角色,一步步实现,每一步都加详细注释,确保你能跟着复现。

3.1 第一步:定义抽象同事类(Colleague)

抽象同事类需要定义组件的通用接口(发送消息、接收消息),并且持有抽象中介者的引用。这里的 “消息” 可以是字符串(比如 “回家模式”“睡眠模式”),也可以是枚举,根据实际需求选择。

#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

// 前向声明抽象中介者,解决循环依赖
class Mediator;

// 抽象同事类:所有智能设备的基类
class Colleague {
protected:
    Mediator* mediator; // 持有抽象中介者的引用
    string name;        // 组件名称(用于日志输出,方便调试)
public:
    // 构造函数:传入中介者和组件名称
    Colleague(Mediator* med, const string& n) : mediator(med), name(n) {}
    virtual ~Colleague() {}

    // 纯虚函数:发送消息(向中介者发送消息)
    virtual void sendMessage(const string& message) = 0;
    // 纯虚函数:接收消息(接收中介者的通知)
    virtual void receiveMessage(const string& message) = 0;

    // 获取组件名称(给中介者使用)
    string getName() const { return name; }
};

3.2 第二步:定义抽象中介者(Mediator)

抽象中介者需要定义两个核心接口:注册同事组件(registerColleague)和转发消息(forwardMessage)。注册接口让中介者能管理所有组件,转发接口让组件的消息能通过中介者传递。

// 抽象中介者:智能家居中介的基类
class Mediator {
public:
    virtual ~Mediator() {}

    // 纯虚函数:注册同事组件
    virtual void registerColleague(Colleague* colleague) = 0;
    // 纯虚函数:转发消息(接收某个组件的消息,协调其他组件)
    virtual void forwardMessage(Colleague* sender, const string& message) = 0;
    // 纯虚函数:执行特定模式(比如回家模式、睡眠模式,方便客户端直接调用)
    virtual void executeMode(const string& mode) = 0;
};

3.3 第三步:实现具体同事类(Concrete Colleague)

咱们实现 4 个具体同事类:灯(Light)、空调(AirConditioner)、窗帘(Curtain)、音响(Speaker)。每个类都实现sendMessagereceiveMessage方法,并且只关注自身的业务逻辑,不关心其他组件。

1. 灯组件(Light)
// 具体同事类:灯
class Light : public Colleague {
public:
    Light(Mediator* med, const string& n) : Colleague(med, n) {}

    // 发送消息:向中介者发送消息(比如"灯已开启")
    void sendMessage(const string& message) override {
        cout << "[" << getName() << "] 发送消息:" << message << endl;
        mediator->forwardMessage(this, message); // 通过中介者转发
    }

    // 接收消息:处理中介者的通知(比如"开启"、"关闭")
    void receiveMessage(const string& message) override {
        cout << "[" << getName() << "] 收到通知:" << message << endl;
        if (message == "开启") {
            turnOn();
        } else if (message == "关闭") {
            turnOff();
        }
    }

    // 自身业务逻辑:开灯
    void turnOn() {
        cout << "[" << getName() << "] 操作结果:灯光已开启" << endl;
    }

    // 自身业务逻辑:关灯
    void turnOff() {
        cout << "[" << getName() << "] 操作结果:灯光已关闭" << endl;
    }
};
2. 空调组件(AirConditioner)
// 具体同事类:空调
class AirConditioner : public Colleague {
private:
    int currentTemp; // 当前温度(新增属性,让示例更真实)
public:
    AirConditioner(Mediator* med, const string& n) : Colleague(med, n), currentTemp(26) {}

    void sendMessage(const string& message) override {
        cout << "[" << getName() << "] 发送消息:" << message << endl;
        mediator->forwardMessage(this, message);
    }

    void receiveMessage(const string& message) override {
        cout << "[" << getName() << "] 收到通知:" << message << endl;
        // 解析消息:比如"调至25℃"
        if (message.find("调至") != string::npos && message.find("℃") != string::npos) {
            int targetTemp = stoi(message.substr(2, message.find("℃") - 2));
            setTemperature(targetTemp);
        } else if (message == "节能模式") {
            setTemperature(28);
        }
    }

    // 自身业务逻辑:调节温度
    void setTemperature(int temp) {
        currentTemp = temp;
        cout << "[" << getName() << "] 操作结果:温度已调节至 " << currentTemp << "℃" << endl;
    }
};
3. 窗帘组件(Curtain)
// 具体同事类:窗帘
class Curtain : public Colleague {
public:
    Curtain(Mediator* med, const string& n) : Colleague(med, n) {}

    void sendMessage(const string& message) override {
        cout << "[" << getName() << "] 发送消息:" << message << endl;
        mediator->forwardMessage(this, message);
    }

    void receiveMessage(const string& message) override {
        cout << "[" << getName() << "] 收到通知:" << message << endl;
        if (message == "拉开") {
            open();
        } else if (message == "拉上") {
            close();
        }
    }

    // 自身业务逻辑:拉开窗帘
    void open() {
        cout << "[" << getName() << "] 操作结果:窗帘已拉开" << endl;
    }

    // 自身业务逻辑:拉上窗帘
    void close() {
        cout << "[" << getName() << "] 操作结果:窗帘已拉上" << endl;
    }
};
4. 音响组件(Speaker)
// 具体同事类:音响
class Speaker : public Colleague {
public:
    Speaker(Mediator* med, const string& n) : Colleague(med, n) {}

    void sendMessage(const string& message) override {
        cout << "[" << getName() << "] 发送消息:" << message << endl;
        mediator->forwardMessage(this, message);
    }

    void receiveMessage(const string& message) override {
        cout << "[" << getName() << "] 收到通知:" << message << endl;
        if (message == "播放音乐") {
            playMusic();
        } else if (message == "停止播放") {
            stopMusic();
        } else if (message == "播放电影音效") {
            playMovieSound();
        }
    }

    // 自身业务逻辑:播放舒缓音乐
    void playMusic() {
        cout << "[" << getName() << "] 操作结果:正在播放舒缓音乐" << endl;
    }

    // 自身业务逻辑:停止播放
    void stopMusic() {
        cout << "[" << getName() << "] 操作结果:已停止播放" << endl;
    }

    // 自身业务逻辑:播放电影音效(新增功能,方便后续扩展)
    void playMovieSound() {
        cout << "[" << getName() << "] 操作结果:正在播放电影环绕音效" << endl;
    }
};

3.4 第四步:实现具体中介者(Concrete Mediator)

具体中介者是核心,需要维护所有同事组件的引用(用unordered_map存储,key 是组件名称,value 是组件指针),并且封装模式切换的逻辑。这里我们实现 “回家模式”“睡眠模式”“离家模式”“影院模式” 四种场景,展示中介者的协调能力。

// 具体中介者:智能家居中介者
class SmartHomeMediator : public Mediator {
private:
    // 存储所有注册的同事组件(名称→组件指针)
    unordered_map<string, Colleague*> colleagues;

    // 私有辅助方法:获取组件(避免重复代码)
    Colleague* getColleague(const string& name) {
        auto iter = colleagues.find(name);
        if (iter != colleagues.end()) {
            return iter->second;
        }
        cout << "警告:未找到组件 " << name << endl;
        return nullptr;
    }

public:
    ~SmartHomeMediator() override {
        // 中介者不负责销毁组件(由客户端管理生命周期)
        colleagues.clear();
    }

    // 注册同事组件:将组件加入中介者的管理
    void registerColleague(Colleague* colleague) override {
        if (colleague == nullptr) return;
        string name = colleague->getName();
        if (colleagues.find(name) == colleagues.end()) {
            colleagues[name] = colleague;
            cout << "中介者已注册组件:" << name << endl;
        } else {
            cout << "警告:组件 " << name << " 已注册" << endl;
        }
    }

    // 转发消息:接收某个组件的消息,协调其他组件
    void forwardMessage(Colleague* sender, const string& message) override {
        cout << "\n=== 中介者收到 [" << sender->getName() << "] 的消息:" << message << " ===" << endl;
        
        // 示例:如果灯发送"开启成功",中介者通知空调自动调温(模拟组件联动)
        if (sender->getName() == "客厅灯" && message == "开启成功") {
            Colleague* ac = getColleague("客厅空调");
            if (ac != nullptr) {
                ac->receiveMessage("调至25℃");
            }
        }
    }

    // 执行特定模式:核心协调逻辑
    void executeMode(const string& mode) override {
        cout << "\n==================== 执行 " << mode << " ====================" << endl;

        if (mode == "回家模式") {
            // 回家模式:灯开、空调25℃、窗帘拉开、音响放音乐
            Colleague* light = getColleague("客厅灯");
            Colleague* ac = getColleague("客厅空调");
            Colleague* curtain = getColleague("客厅窗帘");
            Colleague* speaker = getColleague("客厅音响");

            if (light) light->receiveMessage("开启");
            if (ac) ac->receiveMessage("调至25℃");
            if (curtain) curtain->receiveMessage("拉开");
            if (speaker) speaker->receiveMessage("播放音乐");

        } else if (mode == "睡眠模式") {
            // 睡眠模式:灯关、空调22℃、窗帘拉上、音响关
            Colleague* light = getColleague("客厅灯");
            Colleague* ac = getColleague("客厅空调");
            Colleague* curtain = getColleague("客厅窗帘");
            Colleague* speaker = getColleague("客厅音响");

            if (light) light->receiveMessage("关闭");
            if (ac) ac->receiveMessage("调至22℃");
            if (curtain) curtain->receiveMessage("拉上");
            if (speaker) speaker->receiveMessage("停止播放");

        } else if (mode == "离家模式") {
            // 离家模式:所有设备关闭或节能
            Colleague* light = getColleague("客厅灯");
            Colleague* ac = getColleague("客厅空调");
            Colleague* curtain = getColleague("客厅窗帘");
            Colleague* speaker = getColleague("客厅音响");

            if (light) light->receiveMessage("关闭");
            if (ac) ac->receiveMessage("节能模式");
            if (curtain) curtain->receiveMessage("拉上");
            if (speaker) speaker->receiveMessage("停止播放");

        } else if (mode == "影院模式") {
            // 影院模式:灯关、窗帘拉上、空调24℃、音响放电影音效
            Colleague* light = getColleague("客厅灯");
            Colleague* ac = getColleague("客厅空调");
            Colleague* curtain = getColleague("客厅窗帘");
            Colleague* speaker = getColleague("客厅音响");

            if (light) light->receiveMessage("关闭");
            if (ac) ac->receiveMessage("调至24℃");
            if (curtain) curtain->receiveMessage("拉上");
            if (speaker) speaker->receiveMessage("播放电影音效");

        } else {
            cout << "未知模式:" << mode << endl;
        }

        cout << "=========================================================\n" << endl;
    }
};

3.5 第五步:测试中介者模式的效果

咱们设计一个完整的测试场景,模拟用户切换不同模式,看看中介者模式是否能正常协调所有设备:

// 测试代码:模拟用户操作智能家居系统
int main() {
    // 1. 创建具体中介者
    SmartHomeMediator* mediator = new SmartHomeMediator();

    // 2. 创建具体同事组件,并注册到中介者
    Colleague* livingRoomLight = new Light(mediator, "客厅灯");
    Colleague* livingRoomAC = new AirConditioner(mediator, "客厅空调");
    Colleague* livingRoomCurtain = new Curtain(mediator, "客厅窗帘");
    Colleague* livingRoomSpeaker = new Speaker(mediator, "客厅音响");

    mediator->registerColleague(livingRoomLight);
    mediator->registerColleague(livingRoomAC);
    mediator->registerColleague(livingRoomCurtain);
    mediator->registerColleague(livingRoomSpeaker);

    // 3. 模拟用户切换不同模式
    mediator->executeMode("回家模式");   // 下班回家
    mediator->executeMode("影院模式");   // 晚上看电影
    mediator->executeMode("睡眠模式");   // 准备睡觉
    mediator->executeMode("离家模式");   // 第二天上班

    // 4. 模拟组件主动发送消息(比如灯开启后通知中介者)
    cout << "\n=== 组件主动发送消息测试 ===" << endl;
    livingRoomLight->sendMessage("开启成功");

    // 5. 释放资源(客户端负责销毁组件和中介者)
    delete livingRoomLight;
    delete livingRoomAC;
    delete livingRoomCurtain;
    delete livingRoomSpeaker;
    delete mediator;

    return 0;
}

3.6 测试结果与代码分析

运行上述代码,会输出以下关键结果(已简化冗余日志,突出核心流程):

中介者已注册组件:客厅灯
中介者已注册组件:客厅空调
中介者已注册组件:客厅窗帘
中介者已注册组件:客厅音响

==================== 执行 回家模式 ====================
[客厅灯] 收到通知:开启
[客厅灯] 操作结果:灯光已开启
[客厅空调] 收到通知:调至25℃
[客厅空调] 操作结果:温度已调节至 25℃
[客厅窗帘] 收到通知:拉开
[客厅窗帘] 操作结果:窗帘已拉开
[客厅音响] 收到通知:播放音乐
[客厅音响] 操作结果:正在播放舒缓音乐
=========================================================

==================== 执行 影院模式 ====================
[客厅灯] 收到通知:关闭
[客厅灯] 操作结果:灯光已关闭
[客厅空调] 收到通知:调至24℃
[客厅空调] 操作结果:温度已调节至 24℃
[客厅窗帘] 收到通知:拉上
[客厅窗帘] 操作结果:窗帘已拉上
[客厅音响] 收到通知:播放电影音效
[客厅音响] 操作结果:正在播放电影环绕音效
=========================================================

// 睡眠模式、离家模式输出省略...

=== 组件主动发送消息测试 ===
[客厅灯] 发送消息:开启成功

=== 中介者收到 [客厅灯] 的消息:开启成功 ===
[客厅空调] 收到通知:调至25℃
[客厅空调] 操作结果:温度已调节至 25℃

从测试结果可以看出,中介者模式完美解决了之前的痛点,带来了 4 个显著优势:

  1. 彻底解耦:组件之间没有任何直接引用,灯、空调、窗帘、音响互不认识,所有交互都通过中介者完成。如果要删除某个组件(比如音响),只需要注销它,不会影响其他组件;
  2. 职责清晰:模式切换的逻辑集中在中介者中,客户端只需要调用executeMode方法,不用关心每个设备的具体操作。新增 “健身模式”(灯开、空调调 23℃、音响放动感音乐),只需要在中介者中新增一个mode分支,不用修改任何组件代码;
  3. 扩展性极强:新增设备(比如加湿器),只需要创建Humidifier类继承Colleague,注册到中介者,然后在中介者的模式逻辑中添加对应的通知代码,完全符合 “开闭原则”;
  4. 维护成本低:修改某个模式的逻辑(比如回家模式的空调温度从 25℃改成 24℃),只需要在中介者的executeMode方法中修改一行代码,不用全局搜索所有调用处。

四、中介者模式的进阶技巧:避坑指南 + 实际应用场景

学会了基础实现,咱们还要掌握中介者模式的进阶技巧,避免实际开发中踩坑,同时明确它的适用场景,不要滥用设计模式。

4.1 中介者模式的常见 “坑” 及避坑指南

中介者模式虽然强大,但如果使用不当,会引入新的问题。这几个坑一定要避开:

坑 1:中介者变成 “上帝类”

最常见的问题是把所有逻辑都塞进中介者,导致中介者变得臃肿不堪, responsibilities 过多。比如把组件的业务逻辑(比如灯的开关逻辑)也写在中介者中,违背了 “单一职责原则”。

避坑指南

  • 中介者只负责 “协调”,不负责 “执行”—— 组件的业务逻辑(比如灯的开关、空调调温)必须封装在组件自身,中介者只负责发送通知;
  • 如果中介者的逻辑过于复杂,可以拆分中介者:比如按功能拆分出 “模式管理中介者”“设备联动中介者”,或者按区域拆分出 “客厅中介者”“卧室中介者”。
坑 2:同事类依赖具体中介者,而非抽象中介者

有些开发者会让同事类直接持有具体中介者的引用(比如SmartHomeMediator*),而不是抽象中介者(Mediator*),导致同事类和具体中介者耦合,无法替换其他中介者实现。

避坑指南

  • 严格遵循 “依赖倒置原则”:同事类只能依赖抽象中介者,具体中介者实现抽象接口。这样可以轻松替换中介者,比如把SmartHomeMediator换成HotelRoomMediator(酒店房间中介者),同事类不需要任何修改。
坑 3:滥用中介者模式

如果组件数量少(≤3 个),且交互逻辑简单(比如两个组件之间的简单通信),用中介者模式会 “杀鸡用牛刀”,反而增加系统复杂度。

避坑指南

  • 先评估组件的数量和交互复杂度:只有当组件数量多(≥4 个)、交互频繁、形成网状依赖时,才适合用中介者模式;
  • 简单场景优先用直接通信或观察者模式,复杂协同场景再用中介者模式。
坑 4:忽略中介者的线程安全

在多线程环境中(比如分布式系统的协调中心),多个组件同时向中介者发送消息,可能导致数据竞争或逻辑混乱。

避坑指南

  • 在中介者的forwardMessageexecuteMode方法中添加线程同步机制(比如std::mutex),确保同一时间只有一个消息被处理;
  • 对于异步场景,可以使用消息队列(比如std::queue)缓存消息,中介者异步处理,避免阻塞组件。

4.2 中介者模式的实际应用场景

中介者模式在实际开发中应用非常广泛,只要遇到 “多个组件需要协同工作” 的场景,都可以考虑使用。以下是几个典型场景:

1. GUI 组件交互(桌面应用)

桌面应用的 GUI 组件(比如按钮、文本框、下拉框、对话框)之间经常需要联动。比如 “登录按钮” 点击后,需要验证文本框的用户名和密码,验证通过后关闭登录对话框、打开主窗口。如果这些组件直接通信,会形成复杂的依赖网。用中介者模式,让 “登录窗口中介者” 统一协调所有组件,逻辑更清晰。

2. 分布式系统的协调中心(后端开发)

分布式系统中,多个服务(比如订单服务、支付服务、库存服务、物流服务)需要协同完成一个业务(比如下单流程)。用 “服务中介者”(比如 Spring Cloud 的 Eureka+Feign,或自定义的协调中心)统一管理服务之间的调用,避免服务之间直接耦合。比如订单服务不需要知道库存服务的地址,只需要向中介者发送 “扣减库存” 的消息,中介者会转发给库存服务。

3. 游戏中的场景管理器(游戏开发)

游戏中的场景(比如战斗场景、主城场景)包含多个角色(玩家、NPC、怪物)和物体(道具、障碍物、特效)。这些对象之间需要频繁交互(比如玩家攻击怪物、怪物掉落道具、道具被玩家拾取)。用 “场景中介者”(SceneMediator)统一协调所有对象的交互,每个对象只需要向中介者发送事件(比如 “玩家攻击”),中介者会通知相关对象做出响应(比如怪物掉血、播放攻击特效)。

4. 消息队列的生产者 - 消费者模型(中间件)

消息队列(比如 RabbitMQ、Kafka)本质上就是一种中介者模式。生产者不需要知道消费者的地址,只需要把消息发送到消息队列(中介者),消费者从消息队列获取消息,生产者和消费者完全解耦。消息队列负责消息的转发、缓存、路由,扮演了 “中介者” 的角色。

5. 团队协作系统(办公软件)

比如项目管理工具(Jira)中,项目经理、开发人员、测试人员之间的协作:开发人员提交代码后,需要通知测试人员进行测试;测试人员发现 bug 后,需要通知开发人员修复。用 “项目中介者” 统一管理这些协作流程,每个人只需要向中介者提交操作(比如 “提交代码”“创建 bug”),中介者会自动通知相关人员,避免手动沟通的混乱。

4.3 中介者模式与观察者模式的区别

很多人会把中介者模式和观察者模式搞混,因为它们都涉及 “组件之间的通信”。但两者的核心思想和适用场景完全不同,用一张表格就能清晰区分:

对比维度中介者模式观察者模式
核心目的解耦组件之间的 “多对多协同”解耦 “主题” 与 “观察者” 的 “一对多通知”
交互方式组件通过中介者 “双向通信”(A→中介者→B,B→中介者→A)主题向观察者 “单向通知”(主题→观察者,观察者不反向通知主题)
角色关系中介者与组件是 “管理 - 被管理” 关系主题与观察者是 “发布 - 订阅” 关系
逻辑集中点逻辑集中在中介者(协调逻辑)逻辑分散在观察者(各自的响应逻辑)
典型场景多个组件协同完成复杂功能(如智能家居模式、分布式服务协同)一个对象变化需要通知多个对象(如 UI 控件联动、消息通知)

简单总结:中介者模式是 “多对多协同的协调者”,观察者模式是 “一对多通知的发布者”。比如:

  • 智能家居的 “回家模式” 需要多个设备协同,用中介者模式;
  • 股票 APP 中,股票价格变化需要通知所有关注该股票的用户,用观察者模式。

五、总结:中介者模式的核心价值

看到这里,相信你已经对中介者模式有了全面的理解。咱们最后再总结一下它的核心价值,帮你记住什么时候该用、为什么要用:

  1. 解耦网状依赖:把组件之间的多对多依赖,变成中介者与组件的一对多依赖,让系统结构从 “网状” 变成 “星形”,降低系统复杂度;
  2. 集中协调逻辑:将组件之间的交互逻辑集中在中介者中,避免逻辑散落在各个组件,让代码更易维护、易扩展;
  3. 简化组件通信:组件不需要知道其他组件的存在,只需要和中介者通信,降低了组件的耦合度和学习成本;
  4. 符合开闭原则:新增组件或新增协同逻辑时,只需要扩展中介者或组件,不用修改现有代码。

最后提醒一句:设计模式不是 “银弹”,中介者模式也不例外。它的缺点是中介者可能会变得复杂(如果协调逻辑过多),所以一定要把握好 “协调” 和 “业务” 的边界 —— 中介者只做协调,不做业务。

如果你在实际项目中遇到了 “组件之间乱耦合、协同逻辑难维护” 的问题,不妨试试中介者模式,相信它会让你的代码从 “混乱的团队协作” 变成 “有序的外交官协调”。如果这篇文章对你有帮助,欢迎点赞、收藏,也可以在评论区分享你用中介者模式解决的实际问题~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值