【设计模式手册016】中介者模式 - 解耦多对象交互
本文是「设计模式手册」系列第016篇,我将以深入浅出、追本溯源的风格,带你真正理解中介者模式的精髓。
1. 缘起:我们为何需要中介者模式?
在软件设计中,我们经常会遇到这样的场景:多个对象之间存在复杂的网状交互关系。比如:
- 聊天室系统:多个用户相互发送消息
- 飞机调度系统:飞机、跑道、塔台之间的通信
- UI组件交互:按钮、输入框、下拉菜单的联动
- 微服务架构:服务之间的调用关系
初级程序员的写法:
java
public class User {
private String name;
private List<User> friends;
public User(String name) {
this.name = name;
this.friends = new ArrayList<>();
}
public void addFriend(User friend) {
friends.add(friend);
}
public void sendMessageToAll(String message) {
for (User friend : friends) {
friend.receiveMessage(this, message);
// 每个用户都需要知道如何联系其他用户
}
}
public void sendMessageTo(User toUser, String message) {
toUser.receiveMessage(this, message);
}
public void receiveMessage(User fromUser, String message) {
System.out.println(name + " 收到来自 " + fromUser.name + " 的消息: " + message);
}
}
这种写法的痛点:
- 对象间耦合度高,形成复杂的网状结构
- 一个对象修改会影响多个其他对象
- 难以理解和维护
- 违反迪米特法则(最少知识原则)
2. 中介者模式:本质与定义
2.1 模式定义
中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
2.2 模式结构
java
// 中介者接口
public interface Mediator {
void notify(Object sender, String event);
}
// 抽象组件
public abstract class Component {
protected Mediator mediator;
public Component(Mediator mediator) {
this.mediator = mediator;
}
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
}
// 具体组件
public class ConcreteComponentA extends Component {
public ConcreteComponentA(Mediator mediator) {
super(mediator);
}
public void doA() {
System.out.println("ComponentA 执行 doA()");
mediator.notify(this, "A");
}
public void reactOnB() {
System.out.println("ComponentA 对 B 事件做出反应");
}
}
public class ConcreteComponentB extends Component {
public ConcreteComponentB(Mediator mediator) {
super(mediator);
}
public void doB() {
System.out.println("ComponentB 执行 doB()");
mediator.notify(this, "B");
}
public void reactOnA() {
System.out.println("ComponentB 对 A 事件做出反应");
}
}
// 具体中介者
public class ConcreteMediator implements Mediator {
private ConcreteComponentA componentA;
private ConcreteComponentB componentB;
public void setComponentA(ConcreteComponentA componentA) {
this.componentA = componentA;
}
public void setComponentB(ConcreteComponentB componentB) {
this.componentB = componentB;
}
@Override
public void notify(Object sender, String event) {
if (sender == componentA && "A".equals(event)) {
System.out.println("中介者处理 A 事件,触发 ComponentB 的反应");
componentB.reactOnA();
} else if (sender == componentB && "B".equals(event)) {
System.out.println("中介者处理 B 事件,触发 ComponentA 的反应");
componentA.reactOnB();
}
}
}
3. 深入理解:中介者模式的三重境界
3.1 第一重:从网状结构到星型结构
核心思想:将多对多的复杂关系转换为一对多的简单关系。
// 改造前:网状结构
A ↔ B
↕ ↕
C ↔ D
// 改造后:星型结构
A
|
M(中介者)
/|\
B C D
3.2 第二重:控制逻辑的集中化
设计原则的体现:将对象间的交互逻辑集中到中介者中,让各个组件专注于自己的核心职责。
3.3 第三重:通信协议的标准化
通过中介者定义统一的通信协议,组件只需要与中介者通信,而不需要了解其他组件的细节。
4. 实战案例:完整的聊天室系统
让我们来看一个完整的例子:
java
// 消息类
@Data
@AllArgsConstructor
public class ChatMessage {
private String content;
private User sender;
private LocalDateTime timestamp;
private MessageType type;
public enum MessageType {
TEXT, IMAGE, FILE, SYSTEM
}
}
// 用户类
@Data
public class User {
private String username;
private boolean isOnline;
private LocalDateTime joinTime;
public User(String username) {
this.username = username;
this.isOnline = true;
this.joinTime = LocalDateTime.now();
}
}
// 中介者接口
public interface ChatRoomMediator {
void sendMessage(ChatMessage message);
void addUser(User user);
void removeUser(User user);
void broadcastSystemMessage(String content);
List<User> getOnlineUsers();
}
// 具体中介者 - 聊天室
@Slf4j
public class ChatRoom implements ChatRoomMediator {
private final List<User> users = new ArrayList<>();
private final List<ChatMessage> messageHistory = new ArrayList<>();
private static final int MAX_HISTORY = 1000;
@Override
public void sendMessage(ChatMessage message) {
// 记录消息历史
messageHistory.add(message);
if (messageHistory.size() > MAX_HISTORY) {
messageHistory.remove(0);
}
// 广播给所有在线用户(除了发送者)
for (User user : users) {
if (user.isOnline() && !user.equals(message.getSender())) {
deliverMessage(user, message);
}
}
log.info("消息已广播: {} -> {}",
message.getSender().getUsername(), message.getContent());
}
@Override
public void addUser(User user) {
if (!users.contains(user)) {
users.add(user);
broadcastSystemMessage("用户 " + user.getUsername() + " 加入了聊天室");
log.info("用户 {} 加入聊天室", user.getUsername());
}
}
@Override
public void removeUser(User user) {
if (users.remove(user)) {
user.setOnline(false);
broadcastSystemMessage("用户 " + user.getUsername() + " 离开了聊天室");
log.info("用户 {} 离开聊天室", user.getUsername());
}
}
@Override
public void broadcastSystemMessage(String content) {
ChatMessage systemMessage = new ChatMessage(
content, null, LocalDateTime.now(), ChatMessage.MessageType.SYSTEM
);
for (User user : users) {
if (user.isOnline()) {
deliverSystemMessage(user, systemMessage);
}
}
}
@Override
public List<User> getOnlineUsers() {
return users.stream()
.filter(User::isOnline)
.collect(Collectors.toList());
}
public List<ChatMessage> getMessageHistory(int count) {
int fromIndex = Math.max(0, messageHistory.size() - count);
return new ArrayList<>(messageHistory.subList(fromIndex, messageHistory.size()));
}
private void deliverMessage(User receiver, ChatMessage message) {
// 模拟消息投递 - 实际中可能是WebSocket、消息队列等
String formattedMessage = String.format("[%s] %s: %s",
message.getTimestamp().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
message.getSender().getUsername(),
message.getContent()
);
log.info("投递消息给 {}: {}", receiver.getUsername(), formattedMessage);
}
private void deliverSystemMessage(User receiver, ChatMessage message) {
String formattedMessage = String.format("[系统] %s", message.getContent());
log.info("投递系统消息给 {}: {}", receiver.getUsername(), formattedMessage);
}
}
// 聊天客户端
@Slf4j
public class ChatClient {
private final User user;
private final ChatRoomMediator mediator;
public ChatClient(String username, ChatRoomMediator mediator) {
this.user = new User(username);
this.mediator = mediator;
this.mediator.addUser(user);
}
public void sendMessage(String content) {
ChatMessage message = new ChatMessage(
content, user, LocalDateTime.now(), ChatMessage.MessageType.TEXT
);
mediator.sendMessage(message);
}
public void leave() {
mediator.removeUser(user);
}
public void viewOnlineUsers() {
List<User> onlineUsers = mediator.getOnlineUsers();
log.info("在线用户: {}",
onlineUsers.stream()
.map(User::getUsername)
.collect(Collectors.joining(", ")));
}
}
// 使用示例
public class ChatRoomDemo {
public static void main(String[] args) {
ChatRoomMediator chatRoom = new ChatRoom();
ChatClient alice = new ChatClient("Alice", chatRoom);
ChatClient bob = new ChatClient("Bob", chatRoom);
ChatClient charlie = new ChatClient("Charlie", chatRoom);
alice.sendMessage("大家好!");
bob.sendMessage("你好 Alice!");
alice.viewOnlineUsers();
charlie.leave();
alice.sendMessage("Charlie 走了吗?");
}
}
5. Spring Boot中的优雅实现
在Spring Boot中,我们可以利用事件机制实现中介者模式:
java
// 事件类
@Data
public class OrderEvent {
private String orderId;
private String eventType;
private LocalDateTime timestamp;
private Map<String, Object> data;
public static OrderEvent create(String orderId, String eventType) {
OrderEvent event = new OrderEvent();
event.setOrderId(orderId);
event.setEventType(eventType);
event.setTimestamp(LocalDateTime.now());
event.setData(new HashMap<>());
return event;
}
}
// 事件处理器接口
public interface EventHandler {
void handle(OrderEvent event);
boolean supports(String eventType);
}
// 具体事件处理器
@Component
@Slf4j
public class InventoryUpdateHandler implements EventHandler {
@Override
public void handle(OrderEvent event) {
log.info("库存更新处理器: 处理订单 {} 的 {} 事件",
event.getOrderId(), event.getEventType());
// 更新库存逻辑
}
@Override
public boolean supports(String eventType) {
return "ORDER_CREATED".equals(eventType) || "ORDER_CANCELLED".equals(eventType);
}
}
@Component
@Slf4j
public class NotificationHandler implements EventHandler {
@Override
public void handle(OrderEvent event) {
log.info("通知处理器: 发送订单 {} 的 {} 事件通知",
event.getOrderId(), event.getEventType());
// 发送通知逻辑
}
@Override
public boolean supports(String eventType) {
return "ORDER_CREATED".equals(eventType) || "ORDER_SHIPPED".equals(eventType);
}
}
@Component
@Slf4j
public class PaymentHandler implements EventHandler {
@Override
public void handle(OrderEvent event) {
log.info("支付处理器: 处理订单 {} 的支付相关事件 {}",
event.getOrderId(), event.getEventType());
// 支付处理逻辑
}
@Override
public boolean supports(String eventType) {
return "ORDER_CREATED".equals(eventType) || "ORDER_PAID".equals(eventType);
}
}
// 事件中介者
@Component
@Slf4j
public class OrderEventMediator {
private final List<EventHandler> handlers;
@Autowired
public OrderEventMediator(List<EventHandler> handlers) {
this.handlers = handlers;
}
public void notify(OrderEvent event) {
log.info("事件中介者: 处理 {} 事件,订单 {}",
event.getEventType(), event.getOrderId());
handlers.stream()
.filter(handler -> handler.supports(event.getEventType()))
.forEach(handler -> {
try {
handler.handle(event);
} catch (Exception e) {
log.error("事件处理失败: {} -> {}",
handler.getClass().getSimpleName(), e.getMessage());
}
});
}
}
// 订单服务
@Service
@Slf4j
public class OrderService {
private final OrderEventMediator eventMediator;
public OrderService(OrderEventMediator eventMediator) {
this.eventMediator = eventMediator;
}
public void createOrder(Order order) {
log.info("创建订单: {}", order.getId());
// 订单创建逻辑...
// 发布订单创建事件
OrderEvent event = OrderEvent.create(order.getId(), "ORDER_CREATED");
event.getData().put("order", order);
eventMediator.notify(event);
}
public void cancelOrder(String orderId) {
log.info("取消订单: {}", orderId);
// 订单取消逻辑...
// 发布订单取消事件
OrderEvent event = OrderEvent.create(orderId, "ORDER_CANCELLED");
eventMediator.notify(event);
}
public void shipOrder(String orderId) {
log.info("发货订单: {}", orderId);
// 订单发货逻辑...
// 发布订单发货事件
OrderEvent event = OrderEvent.create(orderId, "ORDER_SHIPPED");
eventMediator.notify(event);
}
}
6. 中介者模式的变体与进阶用法
6.1 分布式中介者模式
在微服务架构中,中介者模式可以演变为消息队列:
java
// 消息生产者
@Component
@Slf4j
public class EventProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void publishEvent(String exchange, String routingKey, Object event) {
log.info("发布事件: {} -> {}", exchange, routingKey);
rabbitTemplate.convertAndSend(exchange, routingKey, event);
}
}
// 消息消费者
@Component
@Slf4j
public class EventConsumer {
@RabbitListener(queues = "${queue.inventory}")
public void handleInventoryEvent(InventoryEvent event) {
log.info("处理库存事件: {}", event.getType());
// 处理库存相关逻辑
}
@RabbitListener(queues = "${queue.notification}")
public void handleNotificationEvent(NotificationEvent event) {
log.info("处理通知事件: {}", event.getType());
// 处理通知逻辑
}
}
6.2 智能中介者模式
中介者可以包含业务逻辑,做出智能路由决策:
java
@Component
@Slf4j
public class SmartWorkflowMediator {
private final Map<String, WorkflowStep> steps = new HashMap<>();
private final Map<String, List<Transition>> transitions = new HashMap<>();
public void registerStep(String stepId, WorkflowStep step) {
steps.put(stepId, step);
transitions.put(stepId, new ArrayList<>());
}
public void addTransition(String fromStep, String toStep, TransitionCondition condition) {
transitions.get(fromStep).add(new Transition(toStep, condition));
}
public void process(WorkflowContext context) {
String currentStep = context.getCurrentStep();
while (currentStep != null) {
WorkflowStep step = steps.get(currentStep);
if (step == null) {
throw new IllegalArgumentException("未知步骤: " + currentStep);
}
log.info("执行步骤: {}", currentStep);
step.execute(context);
// 智能选择下一个步骤
currentStep = findNextStep(currentStep, context);
context.setCurrentStep(currentStep);
}
}
private String findNextStep(String currentStep, WorkflowContext context) {
return transitions.get(currentStep).stream()
.filter(transition -> transition.getCondition().test(context))
.map(Transition::getToStep)
.findFirst()
.orElse(null);
}
}
7. 中介者模式 vs 其他模式
7.1 中介者模式 vs 观察者模式
- 观察者模式:一对多的依赖关系,主题变化时通知所有观察者
- 中介者模式:多对多的复杂交互,通过中介者协调对象间通信
7.2 中介者模式 vs 门面模式
- 门面模式:为复杂子系统提供统一接口,简化客户端调用
- 中介者模式:协调平等对象间的交互,避免它们直接引用
7.3 中介者模式 vs 代理模式
- 代理模式:控制对单个对象的访问,增强功能
- 中介者模式:管理多个对象间的交互关系
8. 总结与思考
8.1 中介者模式的优点
- 降低耦合度:将网状依赖变为星型依赖
- 集中控制逻辑:交互逻辑集中在中介者中,易于维护
- 提高复用性:组件可以独立复用
- 简化对象协议:用中介者代替多对多交互
8.2 中介者模式的缺点
- 中介者可能变得复杂:中介者可能成为上帝对象
- 性能考虑:所有通信都经过中介者,可能成为瓶颈
- 单点故障:中介者故障会影响整个系统
8.3 深入思考
中介者模式的本质是**"交互逻辑的封装"**。在复杂的系统中,对象间的交互往往比对象本身更复杂,将这些交互逻辑抽取到中介者中,可以让系统结构更加清晰。
设计之美的思考:
"中介者模式体现了'分而治之'的思想。将复杂的网状结构变为清晰的星型结构,让每个对象专注于自己的核心职责,而将协调工作交给专门的中介者。"
从源码的角度看,中介者模式在Java标准库和主流框架中都有广泛应用:
- Java Timer 类作为任务的中介者
- Spring 的事件机制
- Java Message Service (JMS)
- MVC框架中的控制器
何时使用中介者模式:
- 对象间存在复杂的引用关系,系统结构混乱
- 一个对象引用很多其他对象,直接通信导致难以复用
- 想通过一个中间类来封装多个类中的行为,又不想生成太多子类
下一篇预告:设计模式手册017 - 备忘录模式:如何优雅地实现对象状态保存与恢复?
版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。