导读:本期聚焦于关中王创作的《C++如何实现中介者模式?用一个中介对象封装对象间交互的完整教程》,敬请观看详情。当系统里十几个类互相持有对方的引用,改一处牵动全身,代码逐渐失控的时候,中介者模式往往是最合适的解药。它通过引入一个中介对象,把原本网状的多对多交互收敛成星形结构,各个同事类只需要认识中介者,彼此之间彻底解耦。本文用C++完整实现中介者模式,先讲清楚它的核心原理和UML结构,再以一个聊天室案例手写代码,涵盖抽象中介者接口、同事基类、具体中介者的设计细节,同时对比观察者模式分析两者的异同,最后给出适用场景与避坑建议,帮你判断什么时候该用它、什么时候会适得其反。

中介者模式(Mediator Pattern)属于行为型设计模式,它的核心思想是:用一个中介对象来封装一系列对象的交互,使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。想象一个机场塔台的例子:一百架飞机之间不需要直接通信,全部通过塔台协调,塔台就是中介者。如果没有塔台,每架飞机都要和其他所有飞机保持联系,复杂度会以平方级增长。软件系统里也是如此,当多个对象之间存在复杂的网状调用关系时,引入中介者能把这个网状结构压扁成星形结构。

C++如何实现中介者模式?用一个中介对象封装对象间交互的完整教程

中介者模式的核心结构与原理

中介者模式的角色划分非常清晰,一共四个核心角色。首先是抽象中介者(Mediator),它定义了一个接口用于与各同事对象之间进行通信;其次是具体中介者(ConcreteMediator),它协调各个同事对象实现协作行为;然后是抽象同事类(Colleague),每个同事类都知道自己的中介者对象;最后是具体同事类(ConcreteColleague),负责实现自身的行为,同时通过与中介者通信来完成与其他同事的交互。

理解这个模式的关键在于弄清楚依赖方向的变化。没有中介者时,同事A想通知同事B,就必须持有B的指针或引用,同事之间形成了双向依赖。引入中介者后,所有同事只依赖中介者接口,中介者依赖所有同事,整个系统的耦合被集中到了一处。这其实是把复杂度转移而不是消灭:对象的交互逻辑从分散在各处的直接调用,集中到了中介者这一个类里。这既是优点也是隐患,如果交互逻辑极其庞大,中介者本身可能变成一个上帝类。

下面是标准中介者模式的类骨架代码,先看抽象层面的定义:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 前向声明
class Colleague;

// 抽象中介者:定义同事对象通信的接口
class Mediator {
public:
    virtual ~Mediator() = default;
    virtual void notify(const std::string& event,
                        Colleague* sender) = 0;
};

// 抽象同事类:持有中介者引用
class Colleague {
protected:
    Mediator* mediator_;
    std::string name_;
public:
    Colleague(Mediator* m, std::string name)
        : mediator_(m), name_(std::move(name)) {}
    virtual ~Colleague() = default;
    virtual void send(const std::string& msg) = 0;
    virtual void receive(const std::string& msg) = 0;
    const std::string& name() const { return name_; }
};

这段代码里有个细节值得注意:Colleague类持有的是Mediator*裸指针,而中介者通常持有同事对象的指针或引用。为什么不用std::shared_ptr?因为中介者和同事之间是双向引用关系,如果双方都用智能指针会形成循环引用,导致内存泄漏。实践中的常见做法是由外部(比如main函数或某个管理器)统一持有同事对象的所有权,中介者内部只保存裸指针做转发。

完整实战:用C++实现一个多人聊天室

聊天室是中介者模式最经典的应用场景。多个用户(同事类)在聊天室(中介者)里发消息,任何一个用户发言,其他所有用户都能收到。用户之间完全不知道彼此的存在,所有消息路由都由聊天室完成。下面是完整可编译的实现:

// 具体同事类:聊天用户
class ChatUser : public Colleague {
public:
    using Colleague::Colleague;

    void send(const std::string& msg) override {
        std::cout << name_ << " 发送: " << msg << std::endl;
        // 不直接联系其他用户,而是通知中介者
        mediator_->notify(msg, this);
    }

    void receive(const std::string& msg) override {
        std::cout << name_ << " 收到: " << msg << std::endl;
    }
};

// 具体中介者:聊天室
class ChatRoom : public Mediator {
private:
    std::vector<Colleague*> users_;
public:
    void addUser(Colleague* user) {
        users_.push_back(user);
    }

    void notify(const std::string& event,
                Colleague* sender) override {
        // 广播给除发送者之外的所有人
        for (auto* u : users_) {
            if (u != sender) {
                u->receive(event);
            }
        }
    }
};

int main() {
    ChatRoom room;  // 中介者由外部创建并掌握生命周期

    ChatUser alice(&room, "Alice");
    ChatUser bob(&room, "Bob");
    ChatUser carol(&room, "Carol");

    room.addUser(&alice);
    room.addUser(&bob);
    room.addUser(&carol);

    alice.send("大家好!");
    bob.send("你好 Alice");

    return 0;
}

运行后,Alice发言,Bob和Carol都会收到消息,Bob回复时Alice和Carol也会收到。注意看send方法的实现:用户只是简单地调用mediator_->notify,它根本不关心聊天室里有谁、消息如何路由。假如现在需求变成私聊功能,只需要修改ChatRoom::notify的路由逻辑即可,所有用户类一行代码都不用动。这就是解耦带来的直接好处。

再看生命周期管理。上面代码中用户对象都是栈上的局部变量,聊天室保存的裸指针在作用域内始终有效,这是最安全的写法。如果用户是动态创建的,建议把所有权交给一个std::vector<std::unique_ptr<ChatUser>>管理,聊天室在用户注销时移除指针。切忌让中介者和同事互相用shared_ptr持有对方,循环引用会让引用计数永远无法归零。

中介者模式与观察者模式的区别及适用场景

很多人会把中介者模式和观察者模式搞混,因为两者都涉及对象间的消息传递。区别在于通信方向的结构:观察者模式是一对多的单向广播,_subject状态变化时通知所有观察者,观察者不能反过来影响_subject;而中介者是双向协调,同事既向中介者报告事件,也接收中介者下达的指令,中介者内部可能维护复杂的业务规则。打个比方,观察者模式像公众号推送,中介者模式像电话总机接线员。

另一个实用角度是看交互是否需要应答。如果只是单纯的状态通知,比如数据变化后刷新几个界面,用观察者模式更轻量;如果对象之间存在有来有回的协作流程,比如用户下订单后库存系统要扣减、日志系统要记录、失败时订单要回滚,这种多对象联动配合的场景就适合中介者模式。经典案例还有MVC架构中的控制器、机场调度系统、对话框中各控件之间的联动。

使用中介者模式也要警惕它的代价。第一,交互逻辑全部堆积在中介者里,如果业务复杂,这个类会迅速膨胀,此时可以考虑把中介者拆分成多个职责更单一的协调器。第二,中介者模式只是把耦合从同事之间转移到了同事与中介者之间,系统整体并没有减少耦合,只是让耦合变得可管理。第三,如果对象之间只有两三个固定的交互关系,直接调用反而更简单清晰,硬套模式属于过度设计。判断标准很朴素:当你发现改动一个类需要连带修改一堆相关的类,或者类图上出现了密密麻麻的双向箭头时,就是中介者模式登场的时候。

最后补充一个现代C++的改进思路:如果同事类型固定且数量少,可以用std::function回调替代虚函数接口,减少继承层次;如果需要严格的事件类型区分,可以用枚举或模板参数对notify的事件进行类型化,避免字符串事件名带来的运行时错误。这些变体在游戏引擎的消息总线、GUI框架的事件分发器中都能见到实际应用,理解了中介者模式的基本骨架,再去读这些框架源码会轻松很多。

C++中介者模式设计模式Mediator模式修改时间:2026-09-11 02:34:35

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0911/54402.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。