设计模式(十八)行为型:中介者模式详解
中介者模式(Mediator Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于通过引入一个中介者对象来封装一组对象之间的交互,从而降低对象间的耦合度,使对象不必显式地相互引用,实现松耦合的协作关系。它将原本“网状”的多对多通信结构转化为“星型”结构,所有对象仅与中介者通信,由中介者负责协调和转发消息。中介者模式广泛应用于图形用户界面(GUI)组件交互、聊天室系统、航空交通管制、多玩家游戏协调、微服务编排、事件总线(Event Bus)等需要集中管理复杂交互逻辑的场景,是构建可维护、可扩展、高内聚低耦合系统的架构利器。
一、详细介绍
中介者模式解决的是“多个对象之间存在复杂的、动态的、多对多的交互关系,导致系统结构混乱、难以理解、修改和扩展”的问题。在传统设计中,对象(如窗口中的按钮、文本框、下拉框)为了响应彼此的状态变化,会直接持有对方的引用并调用其方法。这导致:
- 紧耦合:对象之间相互依赖,修改一个对象可能影响多个其他对象。
- 高复杂度:交互逻辑分散在各个对象中,难以维护。
- 难以复用:对象无法独立使用,必须依赖特定的协作环境。
- 扩展困难:新增对象或交互逻辑需要修改大量现有代码。
中介者模式的核心思想是:“将交互逻辑集中化”,引入一个“中介者”(Mediator)作为所有交互的协调中心。各个协作对象(Colleague)不再直接通信,而是将请求发送给中介者,由中介者根据当前系统状态决定如何处理或转发该请求。
该模式包含以下核心角色:
- Mediator(中介者接口):定义同事对象用来与中介者通信的接口,通常包含一个或多个通知方法(如
notify(sender, event)
)。 - ConcreteMediator(具体中介者):实现
Mediator
接口,知道所有具体同事对象,并负责协调它们之间的交互。它维护同事对象的引用,并在收到通知时执行相应的协调逻辑。 - Colleague(同事类):定义同事对象的基类或接口,持有对中介者的引用。同事对象通过中介者与其他同事通信。
- ConcreteColleague(具体同事类):继承或实现
Colleague
,在自身状态发生变化时,通过中介者发出通知,而不直接调用其他同事的方法。
中介者模式的关键优势:
- 降低耦合度:同事对象之间无直接依赖,仅依赖中介者。
- 集中控制交互逻辑:交互逻辑集中在中介者中,易于理解、修改和扩展。
- 提高可复用性:同事对象可以独立于其他同事被复用。
- 支持动态配置:中介者可以在运行时动态改变对象间的协作方式。
- 简化对象设计:同事对象无需维护与其他对象的引用。
与“观察者模式”相比,中介者关注多对象间的双向协调,观察者关注一对多的单向状态通知;与“命令模式”相比,命令封装单个请求,中介者协调多个对象的协作;与“外观模式”相比,外观为子系统提供统一接口,中介者管理子系统内部组件的交互。
中介者模式适用于:
- 对象间交互复杂且多变。
- 交互逻辑需要集中管理。
- 希望提高对象的独立性和可复用性。
二、中介者模式的UML表示
以下是中介者模式的标准 UML 类图:
图解说明:
Mediator
定义通知接口。ConcreteMediator
知道所有具体同事,并实现协调逻辑。Colleague
持有中介者引用,通过send()
发送事件。- 同事对象通过中介者间接通信。
三、一个简单的Java程序实例及其UML图
以下是一个 GUI 对话框的示例,包含“登录”按钮、“用户名”文本框、“密码”文本框和“记住我”复选框。它们之间的交互由中介者协调。
Java 程序实例
// 中介者接口
interface Mediator {
void notify(Component sender, String event);
}
// 抽象同事组件
abstract class Component {
protected Mediator mediator;
public Component(Mediator mediator) {
this.mediator = mediator;
}
public void send(String event) {
mediator.notify(this, event);
}
public abstract void receive(String event);
}
// 具体同事:按钮
class Button extends Component {
private boolean enabled = false;
private String name;
public Button(Mediator mediator, String name) {
super(mediator);
this.name = name;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
System.out.println("🔘 [" + name + "] 按钮状态: " + (enabled ? "启用" : "禁用"));
}
public boolean isEnabled() {
return enabled;
}
@Override
public void receive(String event) {
if ("enable".equals(event)) {
setEnabled(true);
} else if ("disable".equals(event)) {
setEnabled(false);
}
}
}
// 具体同事:文本框
class TextBox extends Component {
private String text = "";
private String name;
public TextBox(Mediator mediator, String name) {
super(mediator);
this.name = name;
}
public void setText(String text) {
this.text = text;
System.out.println("📝 [" + name + "] 输入: \"" + text + "\"");
// 文本变化时通知中介者
send("textChanged");
}
public String getText() {
return text;
}
@Override
public void receive(String event) {
// 文本框通常不接收外部控制事件
System.out.println("📝 [" + name + "] 收到事件: " + event + " (忽略)");
}
}
// 具体同事:复选框
class Checkbox extends Component {
private boolean checked = false;
private String name;
public Checkbox(Mediator mediator, String name) {
super(mediator);
this.name = name;
}
public void setChecked(boolean checked) {
this.checked = checked;
System.out.println("☑️ [" + name + "] 状态: " + (checked ? "选中" : "未选中"));
// 状态变化时通知中介者
send("checkboxChanged");
}
public boolean isChecked() {
return checked;
}
@Override
public void receive(String event) {
// 复选框通常不接收外部控制事件
System.out.println("☑️ [" + name + "] 收到事件: " + event + " (忽略)");
}
}
// 具体中介者:登录对话框
class LoginDialogMediator implements Mediator {
private Button loginButton;
private TextBox usernameBox;
private TextBox passwordBox;
private Checkbox rememberMeBox;
public LoginDialogMediator(Button loginButton, TextBox usernameBox,
TextBox passwordBox, Checkbox rememberMeBox) {
this.loginButton = loginButton;
this.usernameBox = usernameBox;
this.passwordBox = passwordBox;
this.rememberMeBox = rememberMeBox;
}
@Override
public void notify(Component sender, String event) {
System.out.println("🔄 中介者收到事件: [" + sender.getClass().getSimpleName() +
"] 发出 '" + event + "'");
// 根据发送者和事件类型协调交互
if (sender == usernameBox && "textChanged".equals(event)) {
checkLoginButton();
} else if (sender == passwordBox && "textChanged".equals(event)) {
checkLoginButton();
} else if (sender == rememberMeBox && "checkboxChanged".equals(event)) {
if (rememberMeBox.isChecked()) {
System.out.println("💡 用户选择记住登录状态");
} else {
System.out.println("💡 用户取消记住登录状态");
}
}
}
// 检查是否启用登录按钮
private void checkLoginButton() {
boolean usernameFilled = usernameBox.getText().trim().length() > 0;
boolean passwordFilled = passwordBox.getText().trim().length() > 0;
if (usernameFilled && passwordFilled) {
loginButton.send("enable"); // 通过中介者启用按钮
} else {
loginButton.send("disable");
}
}
// 提供获取组件的方法(可选,用于初始化)
public Button getLoginButton() { return loginButton; }
public TextBox getUsernameBox() { return usernameBox; }
public TextBox getPasswordBox() { return passwordBox; }
public Checkbox getRememberMeBox() { return rememberMeBox; }
}
// 客户端使用示例
public class MediatorPatternDemo {
public static void main(String[] args) {
System.out.println("🔐 登录对话框 - 中介者模式示例\n");
// 创建同事对象
Button loginButton = new Button(null, "登录"); // 初始中介者为 null
TextBox usernameBox = new TextBox(null, "用户名");
TextBox passwordBox = new TextBox(null, "密码");
Checkbox rememberMeBox = new Checkbox(null, "记住我");
// 创建中介者并注入同事对象
LoginDialogMediator mediator = new LoginDialogMediator(
loginButton, usernameBox, passwordBox, rememberMeBox
);
// 将中介者注入到同事对象中
loginButton.mediator = mediator;
usernameBox.mediator = mediator;
passwordBox.mediator = mediator;
rememberMeBox.mediator = mediator;
// 初始状态:登录按钮应禁用
System.out.println("\n--- 初始化 ---");
loginButton.send("disable");
// 模拟用户输入
System.out.println("\n--- 用户输入用户名 ---");
usernameBox.setText("alice");
System.out.println("\n--- 用户输入密码 ---");
passwordBox.setText("secret123");
// 此时登录按钮应被启用
System.out.println("\n✅ 用户名和密码已填写,登录按钮已启用");
// 模拟取消记住我
System.out.println("\n--- 用户取消记住我 ---");
rememberMeBox.setChecked(false);
// 模拟清空密码
System.out.println("\n--- 用户清空密码 ---");
passwordBox.setText("");
// 登录按钮应被禁用
System.out.println("\n✅ 密码已清空,登录按钮已禁用");
}
}
实例对应的UML图(简化版)
运行说明:
LoginDialogMediator
是具体中介者,协调四个 GUI 组件。- 当用户名或密码框内容变化时,发送
textChanged
事件。 - 中介者收到事件后,检查两个框是否都非空,决定启用或禁用登录按钮。
- “记住我”复选框状态变化时,中介者记录用户选择。
- 所有交互逻辑集中在中介者中,组件之间无直接引用。
四、总结
特性 | 说明 |
---|---|
核心目的 | 集中管理对象间交互,降低耦合 |
实现机制 | 星型通信结构,中介者协调转发 |
优点 | 降低耦合、集中控制、提高复用性、简化对象 |
缺点 | 中介者可能变得庞大复杂(“上帝对象”)、增加系统抽象层次 |
适用场景 | GUI 交互、聊天室、游戏协调、工作流引擎、事件总线 |
不适用场景 | 对象间交互简单、交互逻辑稳定、性能极度敏感 |
中介者模式使用建议:
- 避免中介者过度膨胀,可将其拆分为多个子中介者。
- 可结合“观察者模式”实现事件通知机制。
- 在 Java 中,
java.util.Timer
/TimerTask
或事件总线(如 EventBus)是中介者思想的体现。 - 微服务中的 API Gateway 或 Service Mesh 控制平面是分布式中介者。
架构师洞见:
中介者模式是“集中式协调”与“解耦通信”的哲学体现。在现代架构中,其思想已演变为事件驱动架构(EDA)、服务网格(Service Mesh)、API 网关 和 消息中间件 的核心。例如,在微服务中,服务网格的 Sidecar 代理作为中介者,管理服务间的通信、熔断、重试;API 网关作为客户端请求的中介者,负责路由、认证、限流;Kafka 或 RabbitMQ 作为消息中介者,解耦生产者与消费者。未来趋势是:中介者将与AI 编排引擎结合,AI 动态决策服务调用链;在边缘计算中,边缘网关作为本地服务的中介者;在量子网络中,量子中继器是量子态传输的中介者;在元宇宙中,虚拟世界的状态同步依赖于强大的中介协调系统。
掌握中介者模式,有助于设计出高内聚、低耦合、易维护的复杂交互系统。作为架构师,应在面对“多对象协作”或“交互逻辑复杂”时,主动考虑引入中介者。中介者不仅是模式,更是系统治理的枢纽——它提醒我们:真正的可扩展性,来自于将“混乱的网状通信”转化为“有序的星型控制”,让复杂性被封装在单一的协调点,而非散布在系统的每一个角落。