从LoD到中介者模式:中介者如何解耦多对多关系
聊到中介者模式,我得先提一个设计原则——迪米特法则(LoD)。说白了就是:不要和陌生人说话。每个对象只跟它直接相关的对象通信,别绕来绕去。
但现实项目里,对象之间常常是“多对多”的关系。比如一个聊天室,多个用户互相发消息。如果每个用户都直接持有其他用户的引用……嗯,那代码就乱成一锅粥了。
多对多关系的痛点
我早年接手过一个旧系统,里面有个“消息分发”模块。每个模块都直接调用其他模块的接口。结果呢?
- 新增一个模块,要改 N 个地方
- 删除一个模块,要排查所有引用
- 改一个接口,连锁反应
这就是典型的多对多耦合。每个对象都像蜘蛛网上的一个节点,牵一发动全身。
中介者模式登场
中介者模式就是来解决这个问题的。它引入一个中介者对象,让所有对象只跟中介者通信,不直接互相调用。
你想想看,就像现实中的房产中介。买房的人不用挨个找房东,房东也不用自己带人看房。中介统一协调,双方只跟中介打交道。
在代码里,这个模式长这样:
// 中介者接口
class Mediator {
public:
virtual void send(const std::string& msg, Colleague* sender) = 0;
virtual ~Mediator() = default;
};
// 同事类(Colleague)
class Colleague {
protected:
Mediator* mediator_;
public:
explicit Colleague(Mediator* m) : mediator_(m) {}
virtual void send(const std::string& msg) {
mediator_->send(msg, this);
}
virtual void receive(const std::string& msg) = 0;
};
// 具体同事
class User : public Colleague {
private:
std::string name_;
public:
User(const std::string& name, Mediator* m)
: Colleague(m), name_(name) {}
void receive(const std::string& msg) override {
std::cout << name_ << " received: " << msg << std::endl;
}
};
// 具体中介者
class ChatRoom : public Mediator {
private:
std::vector<Colleague*> users_;
public:
void addUser(Colleague* user) {
users_.push_back(user);
}
void send(const std::string& msg, Colleague* sender) override {
for (auto* user : users_) {
if (user != sender) {
user->receive(msg);
}
}
}
};
你看,User 之间不直接通信,全通过 ChatRoom 转发。新增一个用户,只需要注册到聊天室就行,其他用户不用改。
中介者 vs 观察者:到底有啥区别?
这个问题我经常被问到。很多人觉得两者有点像,都是解耦。但本质不同。
| 维度 | 中介者模式 | 观察者模式 |
|---|---|---|
| 通信方向 | 多对多,双向 | 一对多,单向(主题→观察者) |
| 控制中心 | 中介者集中控制逻辑 | 主题只负责通知,不控制 |
| 典型场景 | 聊天室、UI控件协调 | 事件监听、状态变化通知 |
| 耦合度 | 同事之间零耦合 | 观察者依赖主题接口 |
举个例子你就明白了:
- 观察者模式:一个按钮被点击,通知所有监听器。监听器之间互不知道。
- 中介者模式:一个文本框输入内容,中介者决定要不要禁用提交按钮、要不要清空另一个输入框。多个控件之间需要协调。
说白了,观察者适合“广播通知”,中介者适合“复杂交互协调”。
中介者模式的优缺点
任何模式都有代价。中介者模式也不例外。
优点:
- 减少对象之间的耦合,每个对象只依赖中介者
- 集中控制交互逻辑,方便修改和维护
- 对象可以独立变化,互不影响
缺点:
- 中介者可能变得臃肿,承担太多职责
- 如果中介者出问题,整个系统受影响
- 增加了系统的复杂度(多了一个角色)
什么时候用中介者模式?
我个人习惯,遇到以下情况会考虑:
- 对象之间有多对多的交互,且交互逻辑复杂
- 交互逻辑经常变化,需要集中管理
- 对象之间不应该互相知道对方的存在
但别滥用。如果只是两三个对象简单通信,直接调用反而更清晰。
一张图看懂中介者模式
下面这张图展示了中介者模式的核心结构:
从图里能清楚看到:所有同事都只连到中介者,彼此之间没有直接连线。这就是解耦的关键。
总结
中介者模式,说白了就是把“多对多”变成“一对多”。中介者一个人扛起所有协调工作,其他对象只管做好自己的事。
我个人的建议是:先画交互图。如果发现对象之间的连线密密麻麻,那就是中介者该出场的时候了。但别忘了,中介者本身也要控制好复杂度,别让它变成新的“上帝对象”。
嗯,今天就聊到这儿。下次遇到多对多耦合,试试中介者模式,你会回来感谢我的。