【设计模式手册016】中介者模式 - 解耦多对象交互

【设计模式手册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 中介者模式的优点

  1. 降低耦合度:将网状依赖变为星型依赖
  2. 集中控制逻辑:交互逻辑集中在中介者中,易于维护
  3. 提高复用性:组件可以独立复用
  4. 简化对象协议:用中介者代替多对多交互

8.2 中介者模式的缺点

  1. 中介者可能变得复杂:中介者可能成为上帝对象
  2. 性能考虑:所有通信都经过中介者,可能成为瓶颈
  3. 单点故障:中介者故障会影响整个系统

8.3 深入思考

中介者模式的本质是**"交互逻辑的封装"**。在复杂的系统中,对象间的交互往往比对象本身更复杂,将这些交互逻辑抽取到中介者中,可以让系统结构更加清晰。

设计之美的思考

"中介者模式体现了'分而治之'的思想。将复杂的网状结构变为清晰的星型结构,让每个对象专注于自己的核心职责,而将协调工作交给专门的中介者。"

从源码的角度看,中介者模式在Java标准库和主流框架中都有广泛应用:

  • Java Timer 类作为任务的中介者
  • Spring 的事件机制
  • Java Message Service (JMS)
  • MVC框架中的控制器

何时使用中介者模式

  • 对象间存在复杂的引用关系,系统结构混乱
  • 一个对象引用很多其他对象,直接通信导致难以复用
  • 想通过一个中间类来封装多个类中的行为,又不想生成太多子类

下一篇预告:设计模式手册017 - 备忘录模式:如何优雅地实现对象状态保存与恢复?


版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

相关推荐
雨中飘荡的记忆1 小时前
设计模式之门面模式详解
microsoft·设计模式
李日灐1 小时前
C++STL:熟悉vector的底层实现,部分源码解析,迭代器失效和深层次浅拷贝
开发语言·c++
一只小bit1 小时前
Qt 信号与槽:信号产生与处理之间的重要函数
前端·c++·qt·cpp·页面
开心香辣派小星1 小时前
23种设计模式-19策略模式(Strategy Pattern)
java·设计模式·策略模式
十五年专注C++开发1 小时前
sigslot: 一个轻量级实现观察者模式的C++开源库
c++·观察者模式·开源
千千道1 小时前
QT上位机作为FTP客户端上传多文件
c++·qt
屿筱1 小时前
vscode 关于C/C++的环境配置
c++·ide·vscode
程序喵大人1 小时前
C++ MCP 服务器实现
开发语言·c++·项目·mcp服务器
小尧嵌入式1 小时前
QT软件开发知识点流程及文本转语音工具
开发语言·c++·qt