讲解
机场塔台:五十架飞机要起降,如果每架飞机都直接和其余四十九架通话协调,通信线路是灾难级的网状——现实是所有飞机只和塔台说话,塔台居中调度。中介者模式就是这座塔台:用一个中介对象封装一系列对象之间的交互,使各对象不需要显式地相互引用,耦合从「多对多的网」变成「多对一的星」。
没有中介者时,UI 表单是最典型的重灾区:勾选「境外配送」复选框要联动地址栏、关税提示、配送方式下拉三个组件;地址栏一变又要回过来影响关税提示……组件两两互知,加第四个组件要改前三个。有了表单控制器(中介者),每个组件只向控制器汇报「我变了」,由控制器决定通知谁,组件之间互不相识。
结构上两个角色:Mediator 接口(或直接一个具体类)定义同事间通信的协议;Colleague(同事对象)持有中介者引用,自己的状态变化通过中介者转发。注意中介者常常发现自己变成「什么都管的大管家」——这是模式自带的腐化倾向,应对方法是按业务场景拆分多个中介者(登录表单一个、运费计算一个),而不是一个大一统的调度中心。
和外观模式最后分一次工:外观是单向的简化入口(客户端调子系统,子系统不知道客户端),中介者是多向的协调枢纽(同事们通过它互相通信)。前端的「全局事件总线」「状态管理 store」在结构上都带着中介者的影子,差别在于 store 还集中保管了状态本身。
示例
三人聊天室:用户只和 ChatRoom 说话,消息由中介者转发给「除自己外的所有人」:
import assert from 'node:assert/strict';
// 中介者接口:同事间通信的唯一协议
interface ChatMediator {
register(user: ChatUser): void;
broadcast(from: ChatUser, message: string): void;
}
// 同事对象:不认识其他同事,只认识中介者
class ChatUser {
readonly inbox: string[] = [];
constructor(
readonly name: string,
private mediator: ChatMediator | null = null,
) {}
join(mediator: ChatMediator): void {
this.mediator = mediator;
mediator.register(this);
}
send(message: string): void {
this.mediator?.broadcast(this, message);
}
receive(from: string, message: string): void {
this.inbox.push(from + ':' + message);
}
}
class ChatRoom implements ChatMediator {
private readonly users: ChatUser[] = [];
register(user: ChatUser): void {
this.users.push(user);
}
broadcast(from: ChatUser, message: string): void {
for (const user of this.users) {
if (user !== from) {
user.receive(from.name, message);
}
}
}
}
const room = new ChatRoom();
const lin = new ChatUser('小林');
const chen = new ChatUser('小陈');
const zhou = new ChatUser('小周');
lin.join(room);
chen.join(room);
zhou.join(room);
lin.send('晚饭吃什么?');
assert.deepEqual(lin.inbox, []); // 自己收不到自己的消息
assert.deepEqual(chen.inbox, ['小林:晚饭吃什么?']);
assert.deepEqual(zhou.inbox, ['小林:晚饭吃什么?']);
zhou.send('火锅!');
assert.equal(lin.inbox[0], '小周:火锅!');
console.log('小陈的收件箱:' + chen.inbox.join(' | '));
体会一下扩展性:小周是后来加入的,小林和小陈的代码一行没改——新同事只需 join 同一个中介者。如果不用中介者,每加一个用户,所有现存用户都得在通讯录里加上他。网状变星状,这就是中介者模式的核心账。
常见坑
- 中介者膨胀成上帝类:所有交互逻辑都堆进一个 Mediator,它成了新的复杂度黑洞;按业务场景拆分,或者把复杂规则挪回同事对象。
- 同事之间绕过中介者私聊:只要留了一条直连通道,星状结构就开始退化回网状,架构纪律上应禁止。
- 把简单双向调用也中介化:A 调 B、B 调 A 就两个对象时,直接互相引用更直白;中介者为「多对多」准备。
- 中介者里回传引用造成环:同事把 this 传给中介者、中介者回调同事——注意别让序列化(JSON.stringify)撞上循环引用。
- 同步广播处理慢消息:一个 receive 里做重活会拖垮整条广播链;真实聊天室要把投递改成异步队列。
小结
中介者用星状替代网状:同事只和中介者通信,新增同事零改动;警惕中介者自身膨胀。下一章:给对象拍快照、随时回滚的备忘录模式。