mediator模式不可被继承或观察者替代,因其核心是将交互逻辑从参与者中抽离,避免通信职责散落、隐式依赖和网状耦合;c++17下可通过std::any+typeid实现无基类无虚函数的类型擦除式中介者,兼顾动态协调与编译期灵活性。

为什么不用继承或观察者来替代 Mediator
直接用 std::function 或基类虚函数做对象通信,短期快,但很快会演变成“谁调谁、谁依赖谁”理不清。Mediator 的核心不是多加一层类,而是把「交互逻辑」从参与者中抽离——比如 UI 控件间联动(按钮点击禁用输入框、切换 Tab 重置表单),这些规则不该散落在每个控件的 onClick() 里。观察者模式适合一对多广播,但无法表达 A→B→C 的链式协调;而继承会让子类被迫承载无关的通信职责。
如何用 C++17 实现一个无基类、无虚函数的 Mediator
关键在类型擦除 + 编译期注册,避免运行时多态开销和强制继承。参与者不继承任何 MediatorParticipant,只通过模板参数声明自己支持哪些消息:
-
Mediator是一个空模板类,仅靠特化实现不同消息类型分发 - 每个参与者持有一个
std::weak_ptr<mediator>></mediator>,避免循环引用 - 注册用
registerHandler()接收 lambda,内部用std::any存储 handler,靠typeid匹配消息类型 - 发送消息走
notify(const T& msg),只匹配同类型的 handler,不触发 RTTI 遍历
示例:两个类 Button 和 TextField 通过 Mediator<enablemsg></enablemsg> 协调启用状态,无需头文件互相包含,也不用提前声明对方类型。
std::any + typeid 的性能与线程安全怎么处理
单线程下 std::any 搭配 typeid 查找 handler 是 O(1) 均摊(哈希映射到 handler 容器索引);但多线程写入注册需加锁——读多写少场景下,用 std::shared_mutex 比 std::mutex 更合适。真正要注意的是:不要在 handler 中再调用 notify() 形成递归分发,否则 stack overflow 不报错但行为不可控。建议在 notify() 入口加深度计数保护,阈值设为 5 即可。
为什么不要用 std::variant 替代 std::any 来存 handler
std::variant 要求编译期穷举所有可能的 handler 类型,而 Mediator 的使用者是开放的——你无法预知未来会有多少种 lambda 签名(void(const A&)、bool(const B&)、void(A&&))。强行枚举会导致头文件爆炸、编译时间激增,且每次新增消息类型都要改中介者定义。相比之下,std::any 对类型完全透明,配合 std::type_info 运行时比对,更贴合 Mediator “动态协调”的本意。唯一代价是丢失静态类型检查,所以务必在 notify() 内做 any_cast 失败时的断言提示,比如输出 "handler type mismatch for EnableMsg"。
最难的不是实现分发逻辑,而是让团队成员习惯“不直接调用对方接口”,而是先想“这事该发什么消息、由谁响应”。一旦有人绕过 Mediator 直接调用,整个解耦就失效了——这没法靠编译器拦住,得靠 Code Review 和单元测试覆盖消息路径。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











