中介者模式(Mediator Pattern)
一、模式概述
中介者模式是一种行为型设计模式,它定义了一个中介对象来封装一组对象的交互方式。中介者模式使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
核心思想:用一个中介对象来封装一系列对象之间的交互,避免对象之间的直接引用。就像机场的塔台,所有飞机不需要直接互相通信,而是通过塔台来协调。
二、角色说明
| 角色 | 说明 |
|---|---|
| Mediator(抽象中介者) | 定义同事对象注册和通信的接口 |
| ConcreteMediator(具体中介者) | 实现协调逻辑,知道所有同事对象,负责协调它们的交互 |
| Colleague(抽象同事) | 定义同事对象的接口,持有中介者引用 |
| ConcreteColleague(具体同事) | 实现同事对象,通过中介者与其他同事通信 |
三、生活中的例子
- 机场塔台:飞机不直接通信,而是通过塔台协调起降
- 聊天室:用户不直接互相发消息,而是通过聊天室服务器转发
- MVC 架构:Controller 充当 View 和 Model 的中介者
- 房屋中介:买卖双方不直接交易,通过中介者协调
- UI 表单:表单中的各个组件通过中介者协调状态
四、示例1:聊天室系统
问题场景
一个聊天室中,用户发送的消息需要通过聊天室服务器转发给其他用户。
java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// ========== 抽象中介者 ==========
/**
* 聊天室(抽象中介者)
*/
interface ChatRoom {
void register(ChatUser user);
void sendMessage(String message, ChatUser sender);
void sendPrivateMessage(String message, ChatUser sender, String receiverName);
}
// ========== 抽象同事 ==========
/**
* 聊天用户(抽象同事)
*/
abstract class ChatUser {
protected String name;
protected ChatRoom chatRoom;
public ChatUser(String name) {
this.name = name;
}
/**
* 加入聊天室
*/
public void joinRoom(ChatRoom room) {
this.chatRoom = room;
room.register(this);
System.out.println("[" + name + "] 加入聊天室");
}
/**
* 发送消息(公开)
*/
public void send(String message) {
System.out.println("\n[" + name + "] 发送消息: " + message);
chatRoom.sendMessage(message, this);
}
/**
* 发送私聊消息
*/
public void sendTo(String message, String receiverName) {
System.out.println("\n[" + name + "] 发送私聊给 " + receiverName + ": " + message);
chatRoom.sendPrivateMessage(message, this, receiverName);
}
/**
* 接收消息
*/
public abstract void receive(String message, String senderName);
public String getName() { return name; }
}
// ========== 具体同事 ==========
/**
* 普通用户
*/
class NormalUser extends ChatUser {
public NormalUser(String name) {
super(name);
}
@Override
public void receive(String message, String senderName) {
System.out.println(" [" + name + "] 收到来自 " + senderName + " 的消息: " + message);
}
}
/**
* 管理员用户
*/
class AdminUser extends ChatUser {
public AdminUser(String name) {
super(name);
}
@Override
public void receive(String message, String senderName) {
System.out.println(" [管理员 " + name + "] 收到来自 " + senderName + " 的消息: " + message);
}
/**
* 管理员可以发送系统公告
*/
public void sendAnnouncement(String message) {
System.out.println("\n[管理员 " + name + "] 发送系统公告: " + message);
chatRoom.sendMessage("【系统公告】" + message, this);
}
}
// ========== 具体中介者 ==========
/**
* 聊天室服务器(具体中介者)
* 负责消息的转发和用户管理
*/
class ChatRoomServer implements ChatRoom {
private final Map<String, ChatUser> users = new HashMap<>();
@Override
public void register(ChatUser user) {
users.put(user.getName(), user);
System.out.println(" [聊天室] " + user.getName() + " 已注册");
}
@Override
public void sendMessage(String message, ChatUser sender) {
// 将消息转发给除发送者以外的所有在线用户
for (Map.Entry<String, ChatUser> entry : users.entrySet()) {
if (!entry.getKey().equals(sender.getName())) {
entry.getValue().receive(message, sender.getName());
}
}
}
@Override
public void sendPrivateMessage(String message, ChatUser sender, String receiverName) {
ChatUser receiver = users.get(receiverName);
if (receiver != null) {
receiver.receive("[私聊] " + message, sender.getName());
} else {
System.out.println(" [聊天室] 用户 " + receiverName + " 不在线!");
}
}
}
// ========== 客户端代码 ==========
public class ChatRoomDemo {
public static void main(String[] args) {
// 创建聊天室服务器(中介者)
ChatRoomServer server = new ChatRoomServer();
// 创建用户(同事)
ChatUser alice = new NormalUser("Alice");
ChatUser bob = new NormalUser("Bob");
ChatUser charlie = new NormalUser("Charlie");
AdminUser admin = new AdminUser("Admin");
// 加入聊天室
System.out.println("===== 用户加入聊天室 =====");
alice.joinRoom(server);
bob.joinRoom(server);
charlie.joinRoom(server);
admin.joinRoom(server);
// 公开聊天
System.out.println("\n===== 公开聊天 =====");
alice.send("大家好!");
bob.send("你好 Alice!");
// 私聊
System.out.println("\n===== 私聊 =====");
alice.sendTo("Hi Bob, 你好!", "Bob");
// 管理员公告
System.out.println("\n===== 系统公告 =====");
admin.sendAnnouncement("服务器将于今晚10点维护");
}
}运行结果
===== 用户加入聊天室 =====
[聊天室] Alice 已注册
[Alice] 加入聊天室
[聊天室] Bob 已注册
[Bob] 加入聊天室
[聊天室] Charlie 已注册
[Charlie] 加入聊天室
[聊天室] Admin 已注册
[Admin] 加入聊天室
===== 公开聊天 =====
[Alice] 发送消息: 大家好!
[Bob] 收到来自 Alice 的消息: 大家好!
[Charlie] 收到来自 Alice 的消息: 大家好!
[管理员 Admin] 收到来自 Alice 的消息: 大家好!
[Bob] 发送消息: 你好 Alice!
[Alice] 收到来自 Bob 的消息: 你好 Alice!
[Charlie] 收到来自 Bob 的消息: 你好 Alice!
[管理员 Admin] 收到来自 Bob 的消息: 你好 Alice!
===== 私聊 =====
[Alice] 发送私聊给 Bob: Hi Bob, 你好!
[Bob] 收到来自 Alice 的消息: [私聊] Hi Bob, 你好!
===== 系统公告 =====
[管理员 Admin] 发送系统公告: 服务器将于今晚10点维护
[Alice] 收到来自 Admin 的消息: 【系统公告】服务器将于今晚10点维护
[Bob] 收到来自 Admin 的消息: 【系统公告】服务器将于今晚10点维护
[Charlie] 收到来自 Admin 的消息: 【系统公告】服务器将于今晚10点维护五、示例2:UI 表单验证中介者
问题场景
一个注册表单包含用户名、密码、确认密码、邮箱等字段,字段之间有联动验证关系。
java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// ========== 抽象中介者 ==========
/**
* 表单中介者接口
*/
interface FormMediator {
void registerComponent(FormComponent component);
void valueChanged(FormComponent source);
void notifyAll();
}
// ========== 抽象同事 ==========
/**
* 表单组件(抽象同事)
*/
abstract class FormComponent {
protected String name;
protected FormMediator mediator;
protected boolean valid = true;
protected String errorMessage = "";
public FormComponent(String name) {
this.name = name;
}
public void setMediator(FormMediator mediator) {
this.mediator = mediator;
}
public String getName() { return name; }
public boolean isValid() { return valid; }
public String getErrorMessage() { return errorMessage; }
public abstract String getValue();
public abstract void setValue(String value);
public abstract void validate();
@Override
public String toString() {
return valid ? name + ": 正常" : name + ": 错误(" + errorMessage + ")";
}
}
// ========== 具体同事 ==========
/**
* 文本输入框
*/
class TextInput extends FormComponent {
private String value = "";
public TextInput(String name) {
super(name);
}
@Override
public String getValue() { return value; }
@Override
public void setValue(String value) {
this.value = value;
validate();
if (mediator != null) {
mediator.valueChanged(this);
}
}
@Override
public void validate() {
valid = true;
errorMessage = "";
}
}
/**
* 用户名输入框
*/
class UsernameInput extends TextInput {
public UsernameInput() {
super("用户名");
}
@Override
public void validate() {
if (value == null || value.isEmpty()) {
valid = false;
errorMessage = "用户名不能为空";
} else if (value.length() < 3) {
valid = false;
errorMessage = "用户名至少3个字符";
} else if (value.length() > 20) {
valid = false;
errorMessage = "用户名不能超过20个字符";
} else {
valid = true;
errorMessage = "";
}
}
}
/**
* 密码输入框
*/
class PasswordInput extends TextInput {
public PasswordInput() {
super("密码");
}
@Override
public void validate() {
if (value == null || value.isEmpty()) {
valid = false;
errorMessage = "密码不能为空";
} else if (value.length() < 6) {
valid = false;
errorMessage = "密码至少6个字符";
} else {
valid = true;
errorMessage = "";
}
}
}
/**
* 确认密码输入框
* 需要和密码字段联动验证
*/
class ConfirmPasswordInput extends TextInput {
private PasswordInput passwordInput;
public ConfirmPasswordInput(PasswordInput passwordInput) {
super("确认密码");
this.passwordInput = passwordInput;
}
@Override
public void validate() {
if (value == null || value.isEmpty()) {
valid = false;
errorMessage = "确认密码不能为空";
} else if (!value.equals(passwordInput.getValue())) {
valid = false;
errorMessage = "两次输入的密码不一致";
} else {
valid = true;
errorMessage = "";
}
}
}
/**
* 邮箱输入框
*/
class EmailInput extends TextInput {
public EmailInput() {
super("邮箱");
}
@Override
public void validate() {
if (value == null || value.isEmpty()) {
valid = false;
errorMessage = "邮箱不能为空";
} else if (!value.contains("@") || !value.contains(".")) {
valid = false;
errorMessage = "邮箱格式不正确";
} else {
valid = true;
errorMessage = "";
}
}
}
// ========== 具体中介者 ==========
/**
* 注册表单(具体中介者)
* 协调各个表单组件之间的验证关系
*/
class RegistrationForm implements FormMediator {
private final Map<String, FormComponent> components = new HashMap<>();
private final List<FormComponent> componentList = new ArrayList<>();
private Runnable onSubmit;
private Runnable onError;
@Override
public void registerComponent(FormComponent component) {
component.setMediator(this);
components.put(component.getName(), component);
componentList.add(component);
}
@Override
public void valueChanged(FormComponent source) {
// 某个组件值改变时,重新验证所有组件
System.out.println(" [中介者] " + source.getName() + " 值改变,触发联动验证");
notifyAll();
}
@Override
public void notifyAll() {
System.out.println(" [中介者] 开始验证所有组件...");
boolean allValid = true;
for (FormComponent component : componentList) {
component.validate();
System.out.println(" " + component);
if (!component.isValid()) {
allValid = false;
}
}
if (allValid) {
System.out.println(" [中介者] 验证通过!提交按钮可用");
if (onSubmit != null) onSubmit.run();
} else {
System.out.println(" [中介者] 验证失败!提交按钮禁用");
if (onError != null) onError.run();
}
}
public void setOnSubmit(Runnable onSubmit) { this.onSubmit = onSubmit; }
public void setOnError(Runnable onError) { this.onError = onError; }
}
// ========== 客户端代码 ==========
public class FormMediatorDemo {
public static void main(String[] args) {
// 创建表单中介者
RegistrationForm form = new RegistrationForm();
// 创建表单组件
UsernameInput username = new UsernameInput();
PasswordInput password = new PasswordInput();
ConfirmPasswordInput confirmPassword = new ConfirmPasswordInput(password);
EmailInput email = new EmailInput();
// 注册组件
form.registerComponent(username);
form.registerComponent(password);
form.registerComponent(confirmPassword);
form.registerComponent(email);
// 设置提交/错误回调
form.setOnSubmit(() -> System.out.println(" >>> 表单提交成功!"));
form.setOnError(() -> System.out.println(" >>> 表单验证失败,请修正错误"));
// 模拟用户输入
System.out.println("===== 输入用户名 =====");
username.setValue("ab"); // 太短
System.out.println("\n===== 输入密码 =====");
password.setValue("12345"); // 太短
System.out.println("\n===== 输入确认密码 =====");
confirmPassword.setValue("12345678"); // 不一致
System.out.println("\n===== 输入邮箱 =====");
email.setValue("invalid-email"); // 格式错误
System.out.println("\n===== 修正所有输入 =====");
username.setValue("zhangsan");
password.setValue("password123");
confirmPassword.setValue("password123");
email.setValue("zhangsan@example.com");
}
}六、UML 类图
┌──────────────┐
│ Client │──创建──▶ Mediator
└──────────────┘
│
▼
┌──────────────────────────┐ ┌──────────────────────┐
│ Mediator (抽象中介者) │◀──────│ Colleague (抽象同事) │
│ +register() │ 通知 │ -mediator: Mediator│
│ +notify() │◀──────│ +receive() │
└──────────┬───────────────┘ └──────────┬───────────┘
│ 实现 │ 继承
┌──────────┴───────────┐ ┌─────────┴─────────┐
│ ConcreteMediator │ │ConcreteColleague1 │
│ -colleagues: List │───────────▶│ +action() │
│ +notifyAll() │ └───────────────────┘
└──────────────────────┘ ┌───────────────────┐
│ConcreteColleague2 │
│ +action() │
└───────────────────┘七、中介者模式在 Spring 中的应用
java
/**
* Spring MVC 中的 DispatcherServlet 就是典型的中介者模式
*
* DispatcherServlet(中介者)协调:
* - HandlerMapping(处理器映射)
* - HandlerAdapter(处理器适配器)
* - ViewResolver(视图解析器)
* - Controller(控制器)
*
* 请求流程:
* Client -> DispatcherServlet -> HandlerMapping -> Controller
* ↓
* Client <- DispatcherServlet <- ViewResolver <- Model
*/
/**
* Spring Event 也是中介者模式的体现
*
* ApplicationEventPublisher 充当中介者
* ApplicationEvent 是消息
* ApplicationListener 是同事对象
*
* @Component
* public class OrderEventPublisher {
* @Autowired
* private ApplicationEventPublisher publisher;
*
* public void createOrder(Order order) {
* // ... 业务逻辑
* publisher.publishEvent(new OrderCreatedEvent(order));
* }
* }
*
* @Component
* public class OrderEventHandler {
* @EventListener
* public void handleOrderCreated(OrderCreatedEvent event) {
* // 处理订单创建事件
* }
* }
*/八、优缺点分析
优点
- 减少类间耦合:同事对象之间不需要直接引用,通过中介者通信
- 集中控制:交互逻辑集中在中介者中,便于修改和维护
- 符合迪米特法则:同事对象只需要知道中介者,不需要知道其他同事
- 易于扩展:新增同事对象只需要在中介者中注册即可
缺点
- 中介者过于复杂:随着同事对象增多,中介者可能变得非常复杂(上帝类)
- 单点故障:中介者一旦出问题,整个系统都会受影响
- 性能问题:所有交互都通过中介者,可能成为性能瓶颈
九、使用场景
- 聊天系统:消息通过服务器转发
- MVC 架构:Controller 充当 View 和 Model 的中介
- 飞机场塔台:协调飞机的起降
- 表单验证:表单组件之间的联动验证
- 事件总线:通过事件总线实现组件之间的解耦通信
- 微服务网关:API Gateway 充当服务之间的中介
十、与其他模式的关系
- 观察者模式 vs 中介者模式:观察者模式是多对多,中介者模式是多对一
- 代理模式 vs 中介者模式:代理控制对单一对象的访问,中介者协调多个对象的交互
- 外观模式 vs 中介者模式:外观简化子系统接口,中介者协调同事对象的交互
- 观察者模式 + 中介者模式:中介者可以用观察者模式来实现通知机制
十一、最佳实践
- 保持中介者的简洁性:不要让中介者承担过多的职责
- 合理使用事件机制:结合观察者模式,使用事件来解耦
- 考虑性能影响:在高频交互场景中,中介者可能成为瓶颈
- 避免中介者成为上帝类:可以通过分层中介者来降低复杂度
- 使用接口解耦:同事对象依赖抽象中介者接口,而不是具体实现
java
// 推荐:依赖抽象中介者
interface Mediator {
void notify(Colleague sender, String event);
}
class ConcreteColleague extends Colleague {
private Mediator mediator; // 依赖接口
public ConcreteColleague(Mediator mediator) {
super(mediator);
}
}总结:中介者模式的核心是"集中控制对象间的交互"。它将网状的多对多交互转化为星形的一对多交互,大大降低了对象之间的耦合度。在实际应用中,MVC 架构、聊天系统、事件总线等都是中介者模式的典型应用。使用时要注意保持中介者的职责清晰,避免使其成为过于复杂的上帝类。