中介者模式有哪些角色?调用流程是什么?
简化版
中介者模式主要有中介者接口、具体中介者、同事对象三个角色。调用流程是同事对象把事件交给中介者,中介者根据规则协调其他同事对象。
详细版
中介者模式的角色通常包括:
- Mediator:定义同事对象通知中介者的方法;
- ConcreteMediator:持有多个同事对象,集中处理协作逻辑;
- Colleague:参与协作的对象,只依赖中介者,不直接依赖其他同事对象。
典型流程是:
- 某个同事对象发生动作;
- 它调用中介者方法,比如
changed(this)或notify(event); - 中介者判断事件来源和业务规则;
- 中介者调用其他相关同事对象完成联动。
这样做的好处是,同事对象之间不需要互相知道,复杂交互集中在中介者中维护。
完整版教学
一、为什么角色划分很重要
中介者模式看起来像“对象都找一个中间人说话”,但真正写代码时,容易写成两种坏结构:
- 同事对象仍然互相调用,中介者只是摆设;
- 中介者吞掉所有业务能力,变成一个巨大的流程类。
所以理解角色边界非常关键。
中介者模式的重点是:同事对象负责自身行为,中介者负责协作关系。
二、Mediator:通信入口
Mediator 是同事对象和协调逻辑之间的接口。
它通常长这样:
interface DialogMediator {
void componentChanged(Component component);
}
也可以按事件类型设计:
interface Mediator {
void notify(String event, Object data);
}
接口的作用是降低同事对象对具体中介者的依赖。同事对象只知道“我把变化告诉中介者”,不关心中介者里面如何协调。
三、ConcreteMediator:协作规则中心
具体中介者负责真正的协调逻辑。
例如登录弹窗里有用户名输入框、密码输入框、登录按钮:
- 用户名为空时禁用登录按钮;
- 密码为空时禁用登录按钮;
- 两者都有值时启用登录按钮。
这些规则如果写在输入框里,输入框就要知道按钮;如果写在按钮里,按钮又要知道输入框。更合适的做法是放在中介者里。
class LoginDialogMediator implements DialogMediator {
private TextBox username;
private TextBox password;
private Button loginButton;
public void setComponents(TextBox username, TextBox password, Button loginButton) {
this.username = username;
this.password = password;
this.loginButton = loginButton;
}
@Override
public void componentChanged(Component component) {
boolean enable = username.hasText() && password.hasText();
loginButton.setEnabled(enable);
}
}
这里中介者知道多个组件,也知道组件之间的联动规则。
四、Colleague:同事对象
同事对象是参与协作的业务对象。
它一般持有中介者引用:
abstract class Component {
protected DialogMediator mediator;
protected Component(DialogMediator mediator) {
this.mediator = mediator;
}
}
具体组件只在自己变化时通知中介者:
class TextBox extends Component {
private String text = "";
public TextBox(DialogMediator mediator) {
super(mediator);
}
public void setText(String text) {
this.text = text;
mediator.componentChanged(this);
}
public boolean hasText() {
return text != null && !text.isBlank();
}
}
注意,TextBox 不需要知道 Button,这就是解耦效果。
五、调用流程怎么串起来
完整流程可以这样描述:
用户输入用户名
↓
TextBox.setText()
↓
TextBox 通知 mediator.componentChanged(this)
↓
Mediator 读取 username/password 状态
↓
Mediator 设置 loginButton 是否可点击
面试时讲清楚这个流程,比只背角色名更有说服力。
六、角色边界怎么把握
一个实用判断是:
- “我自己的状态怎么变”属于同事对象;
- “我变了以后谁要跟着变”属于中介者;
- “多个对象之间的协作流程”属于中介者;
- “单个对象内部的核心能力”不应该被中介者拿走。
如果中介者开始处理每个组件内部细节,就说明边界失控了。
七、常见误区与追问
角色流程从 Colleague 产生事件开始。C1 不直接调用 C2,而是把事件交给 Mediator;Mediator 读取必要上下文后,通知 C2 或 C3 执行动作。以4个控件为例,每个控件只保存一个中介者引用,协调规则集中在对应对话框中介者。
| 检查维度 | 判定依据 |
|---|---|
| Mediator | 声明并执行协调协议 |
| ConcreteColleague | 处理自身逻辑并报告交互事件 |
C1 --event--> M --command--> C2/C3
记忆钩子:同事上报,中介判断,同事执行。
- 误区:Mediator 只是消息转发器。 经典中介者可以根据状态和事件选择不同协作路径。
- 追问:同事如何获得 Mediator? 构造注入、注册或创建阶段装配均可,优先依赖接口。
- 误区:Mediator 必须维护所有同事集合。 固定场景可持有明确引用,动态场景才需要注册表。
- 追问:调用流程可以是异步吗? 可以,但必须定义事件顺序、失败与一致性语义。
- 追问:如何验证依赖方向? 检查任何 Colleague 都不应引用另一个具体 Colleague。
八、加强记忆
中介者模式的角色可以记成“同事发事件,中介做协调”。同事对象只负责自己的状态和行为,变化时通知中介者;具体中介者保存相关对象并执行协作规则;客户端看到的是对象联动变简单了,而代码内部减少的是对象之间互相持有、互相调用的网状依赖。