中介者模式如何降低同事对象之间的耦合?
简化版
中介者模式通过让同事对象只依赖中介者,而不直接依赖其他同事对象,把多对多的网状依赖变成多对一的星型依赖。这样新增或修改交互规则时,主要改中介者,而不是到处改同事对象。
详细版
没有中介者时,对象之间可能互相持有引用:
A -> B, C
B -> A, C
C -> A, B
这会导致:
- 对象知道太多其他对象;
- 改一个交互规则要改多个类;
- 新增对象时要修改很多已有对象;
- 单元测试也更麻烦。
引入中介者后:
A -> Mediator
B -> Mediator
C -> Mediator
Mediator -> A, B, C
同事对象只通知中介者,由中介者决定后续动作。耦合并没有消失,而是从很多对象之间转移并集中到中介者中管理。
完整版教学
一、先理解“耦合”到底在哪里
在多个对象协作时,耦合通常体现在三件事上:
- 知道对方存在:A 类里直接持有 B、C、D;
- 知道对方接口:A 要调用 B 的方法,还要知道参数和调用顺序;
- 知道协作规则:A 知道自己变化后要通知谁、以什么条件通知。
中介者模式主要减少的是前两类耦合,并把第三类协作规则集中起来。
二、没有中介者时的网状依赖
假设一个表单里有三个组件:
CountrySelect:国家下拉框;CitySelect:城市下拉框;SubmitButton:提交按钮。
如果国家变化后要刷新城市,城市变化后要判断按钮状态,按钮状态还要看国家和城市是否都选择,直接写可能变成:
class CountrySelect {
private CitySelect citySelect;
private SubmitButton submitButton;
public void changeCountry(String country) {
citySelect.reload(country);
submitButton.refreshState();
}
}
这样 CountrySelect 不再只是国家选择组件,它还知道城市组件和按钮组件。
组件越多,引用越多。
三、引入中介者后的依赖方向
可以改成:
class CountrySelect {
private FormMediator mediator;
public void changeCountry(String country) {
mediator.countryChanged(country);
}
}
然后在中介者里协调:
class FormMediator {
private CitySelect citySelect;
private SubmitButton submitButton;
public void countryChanged(String country) {
citySelect.reload(country);
submitButton.refreshState();
}
}
现在 CountrySelect 不再知道 CitySelect 和 SubmitButton。它只知道中介者。
四、耦合是被管理,不是凭空消失
要注意一个很关键的点:中介者模式不是让耦合彻底消失,而是把分散耦合集中管理。
原来耦合散落在 A、B、C、D 中:
A 里有 B/C 规则
B 里有 A/D 规则
C 里有 A/B/D 规则
现在集中在中介者:
Mediator 里有 A/B/C/D 的协作规则
这是一种取舍。
好处是同事对象变简单,交互规则集中;代价是中介者会变复杂。因此中介者也需要良好拆分,不能无限膨胀。
五、对测试和维护的影响
引入中介者后,测试可以分成两类:
- 单测同事对象:验证它是否正确通知中介者;
- 单测中介者:验证不同事件下是否协调正确对象。
这样比测试一堆互相调用的对象要清晰。
维护时也更容易定位问题:
- 单个对象自身行为错了,看同事对象;
- 多个对象联动错了,看中介者。
六、常见误区与追问
中介者减少的是同事对象之间的直接依赖边,不是让对象完全没有依赖。若5个组件两两通信,最坏可能出现10条无向关系;引入中介者后,每个组件只连接中介者,核心连接约5条。代价是交互规则集中到中介者,必须避免它演化成包含所有业务的上帝对象。
| 检查维度 | 判定依据 |
|---|---|
| 直接交互 | 同事知道多个具体同事 |
| 经中介者 | 同事只报告事件并接收协调结果 |
关系数:n(n-1)/2 -> n
记忆钩子:同事不再互相“打电话”,统一向调度台报告。
- 误区:解耦后 Colleague 不依赖任何对象。 它通常仍依赖 Mediator 接口,只是不依赖其他具体同事。
- 追问:为什么依赖边会减少? 网状的同事间连接被收敛为每个同事到中心的一条连接。
- 误区:所有同事行为都应搬进中介者。 同事自身规则应保留,中介者只协调跨对象交互。
- 追问:中介者变化频繁说明什么? 可能承担过多业务,需按用例拆分或下沉领域规则。
- 追问:如何测试解耦效果? 替换某个 Colleague 实现,确认其他同事无需感知其具体类型。
七、加强记忆
中介者模式降低耦合的方式,是让同事对象不再直接认识彼此,而是统一通过中介者协作。它把网状依赖改成星型依赖,耦合从“到处都有”变成“集中管理”。这带来可维护性提升,但也要求中介者保持清晰边界,避免变成上帝类。