轻量级中介者接口应仅定义具体协作动作(如notifyUserLogin),用纯虚函数+值语义参数,不持有参与者、不管理生命周期、避免模板化与框架耦合。

中介者接口怎么定义才够轻量
轻量级中介者的核心是避免把所有交互逻辑塞进一个大类,关键在于只暴露对象间必须通信的最小契约。接口里不该有 sendAll、broadcast 这类泛化方法,而应按具体协作场景拆成明确动作,比如 notifyUserLogin、requestResourceRelease。
常见错误是让中介者继承 QObject 或强耦合某框架——这会污染纯逻辑层。真正轻量的做法是用纯虚函数 + 值语义参数(如 std::string_view、int),不持有任何参与者的指针或引用。
- 接口只声明行为,不管理生命周期:所有参与者自行注册/注销,中介者不
delete也不shared_ptr持有 - 避免模板化接口:虽然能提升类型安全,但会让调用方编译依赖爆炸,普通
enum class+std::variant更可控 - 参数尽量传值或只读视图,禁止
const T&隐式延长临时对象生命期(尤其在异步回调中易踩坑)
ConcreteMediator 如何避免变成上帝对象
实现类最容易膨胀成“万能调度器”,根源在于把状态判断和业务规则全堆进去。正确做法是把决策逻辑下沉:中介者只做路由与协调,不包含“如果用户等级>5就发奖励”这类规则。
例如,两个 ChatUser 对象要建立会话,中介者只需调用 sessionManager->createSession(u1, u2),而不是自己 new 一个 Session 并维护其状态。
- 用策略对象隔离规则:把权限校验、配额检查等抽成独立
Policy类,中介者只持有一个std::unique_ptr - 注册表用
std::unordered_map<:type_index std::any>而非巨型 switch:支持运行时插拔组件,避免修改中介者代码就能接入新模块 - 禁止在
handleMessage()中直接调用其他参与者的非接口函数(如user->internalState()),这等于绕过封装
同事类如何安全注册与解耦通信路径
同事类(Colleague)不是被动接收消息的“终端”,而是主动向中介者表达意图的协作者。注册方式直接影响耦合度:传裸指针最危险,传 std::weak_ptr 又增加内存管理负担。
推荐用“句柄+回调”模式:中介者返回一个 RegistrationHandle(本质是 size_t ID),同事类用它来注销;通信全部走 std::function 回调,中介者内部用 std::vector 存储这些回调而非对象指针。
- 注册时传入的 lambda 必须是无捕获或仅捕获 this(且确保 this 生命周期长于中介者)
- 注销必须显式调用,不能依赖析构——因为中介者可能比同事活得久,未注销会导致悬空回调
- 消息类型
Msg推荐用struct+std::optional字段,比std::variant内存更紧凑,也避免访问错误类型时抛异常
为什么不用 std::function 就容易内存泄漏
很多实现用 std::function 存回调却没处理捕获变量的生命周期,典型现象是程序退出前出现 double free 或 use-after-free 错误信息,尤其在同事类提前析构后中介者还尝试调用其回调时。
根本原因是 std::function 会拷贝捕获的变量,若捕获了 this 指针,而对象已销毁,后续调用就会崩溃。解决方案不是禁用 std::function,而是加一层间接层。
- 同事类注册时传入一个
std::shared_ptr的弱引用包装器(如std::weak_ptr),回调内先lock()再执行 - 中介者内部用
std::vector<:pair std::function>>>,但每个std::function都是通过std::bind绑定到弱引用上,不是直接绑定 this - 测试时故意让同事类早于中介者析构,观察是否触发断言或日志——这是验证解耦是否真生效的关键检查点
真正难的不是写出能编译的中介者,而是让每个同事类在不引入头文件依赖、不感知其他同事存在的情况下,还能完成协作。这要求从第一行注册代码开始,就拒绝“方便一点”的捷径。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











