大多数情况下不需要手写中介者模式——标准库和现代C++(如std::function+std::shared_ptr)已覆盖80%解耦需求,硬套GoF结构反而增加冗余类和虚函数开销。

中介者模式在C++里到底要不要自己手写
大多数情况下,不需要——除非你明确需要控制对象间通信的时序、拦截消息、做统一日志或状态协调。标准库和现代C++(比如 std::function + std::shared_ptr)已经能覆盖80%的解耦需求,硬套经典四人帮(GoF)中介者结构反而增加冗余类和虚函数开销。
Mediator 接口设计的关键取舍
别一上来就定义纯虚 notify() 和一堆 handleXXX() 方法。真实项目中,中介逻辑往往只集中在1–2种事件类型(如“配置更新”“用户登录完成”),过度抽象反而让调用方难以追溯数据流向。
- 推荐用泛型回调注册:接收
std::function<void std::string event const nlohmann::json data></void>,比继承接口更灵活 - 避免在
Mediator中持有具体同事(Colleague)指针——改用std::weak_ptr防循环引用 - 如果同事生命周期固定(如单例模块),直接传引用比智能指针更轻量
一个够用的最小实现(无继承、无宏、C++17)
下面这个版本去掉所有“模式感”,只保留核心:集中分发、弱引用管理、线程安全注册/注销。
class Mediator {
public:
using Callback = std::function<void(const std::string&, const nlohmann::json&)>;
<pre class='brush:php;toolbar:false;'>template<typename T>
void subscribe(const std::string& event, T&& cb) {
std::lock_guard<std::mutex> lk(mtx_);
callbacks_[event].emplace_back(std::forward<T>(cb));
}
void notify(const std::string& event, const nlohmann::json& data) {
std::lock_guard<std::mutex> lk(mtx_);
auto it = callbacks_.find(event);
if (it != callbacks_.end()) {
for (auto& cb : it->second) {
cb(event, data);
}
}
}private:
std::unorderedmap<std::string, std::vector
使用时:mediator.subscribe("user_logged_in", [](const auto&, const auto& d) { /* 处理登录后动作 */ }); —— 不需要定义 UserColleague 或 ChatColleague 类,同事直接按需订阅事件。
立即学习“C++免费学习笔记(深入)”;
容易被忽略的三个实际问题
很多人卡在“对象怎么知道该找哪个中介者”,或者“注销时崩溃”,本质是生命周期没对齐。
- 中介者实例通常应为全局单例或由顶层容器(如
App类)持有,不能由某个同事临时创建 - 用
std::weak_ptr管理回调中的对象引用时,必须在调用前检查.lock()是否非空,否则访问悬挂指针 - 如果事件参数含非 trivial 类型(如自定义结构体),优先序列化为
nlohmann::json或std::variant,避免模板实例爆炸和 ABI 兼容问题
真正难的从来不是写出符合UML图的代码,而是让不同模块的开发者愿意用同一套事件名、同一套数据格式去通信——这靠的是约定,不是模式。


















