1. 中介者模式:为什么我们需要一个"中间人"?
想象一下这样的场景:你正在开发一个复杂的航空管制系统,数十架飞机需要实时交换位置、速度、航线变更等信息。如果让每架飞机都直接与其他所有飞机通信,系统很快就会变成一团乱麻。这正是中介者模式(Mediator Pattern)要解决的问题——用一个中介对象来封装一系列对象之间的交互。
在面向对象设计中,我们常常会遇到多个对象相互引用、彼此调用的网状结构。这种结构虽然直观,但随着系统复杂度上升,会带来几个致命问题:
- 可维护性灾难:对象间的耦合度过高,修改一个类可能引发连锁反应
- 复用困难:由于对象间强关联,很难单独复用某个类
- 理解成本高:开发人员需要跟踪大量交叉引用才能理解系统行为
中介者模式通过引入一个中介者对象,将网状结构转化为星型结构。所有对象只与中介者通信,由中介者负责协调各对象间的交互。这就像把一群争吵的人交给一个会议主持人管理,效率会大幅提升。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 中介者模式的核心结构与角色解析
2.1 UML类图与组件职责
让我们先看中介者模式的标准UML结构:
code复制[Colleague] <--- [Mediator] ---> [Colleague]
| |
[ConcreteColleague] [ConcreteColleague]
关键角色包括:
- Mediator(抽象中介者):定义同事对象到中介者对象的接口
- ConcreteMediator(具体中介者):实现抽象中介者的接口,协调各同事对象
- Colleague(抽象同事类):定义同事对象的接口,持有中介者引用
- ConcreteColleague(具体同事类):实现抽象同事类,与其他同事通过中介者通信
2.2 模式实现的Java代码骨架
以下是中介者模式的基本Java实现框架:
java复制// 抽象中介者
interface Mediator {
void register(Colleague colleague);
void relay(Colleague colleague, String message);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private List<Colleague> colleagues = new ArrayList<>();
@Override
public void register(Colleague colleague) {
colleagues.add(colleague);
colleague.setMediator(this);
}
@Override
public void relay(Colleague sender, String message) {
for (Colleague colleague : colleagues) {
if (colleague != sender) {
colleague.receive(message);
}
}
}
}
// 抽象同事类
abstract class Colleague {
protected Mediator mediator;
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public abstract void send(String message);
public abstract void receive(String message);
}
// 具体同事类
class ConcreteColleagueA extends Colleague {
@Override
public void send(String message) {
System.out.println("同事A发送消息:" + message);
mediator.relay(this, message);
}
@Override
public void receive(String message) {
System.out.println("同事A收到消息:" + message);
}
}
3. 实战:用中介者模式重构聊天室系统
3.1 问题场景分析
假设我们需要开发一个多用户聊天室,用户可以直接互相发送消息。初始设计可能是这样的:
java复制class User {
private String name;
private List<User> friends = new ArrayList<>();
public void sendMessage(User receiver, String message) {
System.out.println(name + " 发送给 " + receiver.name + ": " + message);
receiver.receiveMessage(this, message);
}
public void receiveMessage(User sender, String message) {
System.out.println(name + " 收到来自 " + sender.name + ": " + message);
}
// 添加/删除好友等方法...
}
这种设计的问题在于:
- 用户间直接耦合
- 新增功能(如群发、屏蔽)需要修改User类
- 难以扩展新类型的消息处理
3.2 使用中介者模式重构
引入ChatRoom作为中介者:
java复制// 中介者接口
interface ChatMediator {
void sendMessage(User sender, String message, User receiver);
void broadcast(User sender, String message);
void registerUser(User user);
}
// 具体聊天室实现
class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void registerUser(User user) {
users.add(user);
user.setChatMediator(this);
}
@Override
public void sendMessage(User sender, String message, User receiver) {
if (users.contains(receiver)) {
receiver.receive(sender, message);
}
}
@Override
public void broadcast(User sender, String message) {
for (User user : users) {
if (user != sender) {
user.receive(sender, message);
}
}
}
}
// 用户类
class User {
private String name;
private ChatMediator chatMediator;
public void setChatMediator(ChatMediator mediator) {
this.chatMediator = mediator;
}
public void send(String message, User receiver) {
chatMediator.sendMessage(this, message, receiver);
}
public void broadcast(String message) {
chatMediator.broadcast(this, message);
}
public void receive(User sender, String message) {
System.out.println(name + " 收到来自 " + sender.name + ": " + message);
}
}
重构后的优势:
- 用户间解耦,只依赖中介者
- 新增功能只需修改ChatRoom类
- 可以轻松扩展不同类型的消息处理
4. 中介者模式的进阶应用与最佳实践
4.1 与观察者模式的结合使用
中介者模式常与观察者模式结合,形成更灵活的事件处理机制。例如,我们可以让中介者维护一个事件总线:
java复制class EventBusMediator {
private Map<String, List<Consumer<String>>> eventHandlers = new HashMap<>();
public void subscribe(String eventType, Consumer<String> handler) {
eventHandlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
}
public void publish(String eventType, String eventData) {
List<Consumer<String>> handlers = eventHandlers.get(eventType);
if (handlers != null) {
handlers.forEach(handler -> handler.accept(eventData));
}
}
}
4.2 中介者模式的性能考量
虽然中介者模式能降低耦合度,但需要注意:
- 单点瓶颈风险:所有交互都经过中介者,可能成为性能瓶颈
- 内存占用:中介者通常需要维护所有同事对象的引用
- 事件风暴:高频小消息可能导致中介者过载
优化建议:
- 对高频操作提供批量接口
- 考虑异步非阻塞实现
- 对不重要的事件提供"尽力而为"的传输保证
4.3 实际项目中的使用场景判断
适合使用中介者模式的典型场景包括:
- GUI组件交互:如对话框中的按钮、输入框等控件协调
- 游戏开发:角色、道具、场景等游戏对象间的交互
- 工作流引擎:多个处理节点间的协调
- 分布式系统:服务发现与协调
不适合的场景:
- 对象间交互非常简单直接
- 性能是首要考虑因素
- 对象间的关系本来就是松耦合的
5. 中介者模式的常见误区与避坑指南
5.1 过度设计陷阱
新手常犯的错误是过早引入中介者模式。判断是否需要中介者的简单标准:
- 对象间的交互是否频繁且复杂?
- 修改一个类是否经常需要修改其他类?
- 是否难以单独测试某个类?
如果以上问题的答案都是"否",那么可能不需要中介者模式。
5.2 中介者类的膨胀问题
随着系统演进,中介者类可能变得过于庞大。解决方法:
- 分而治之:按功能划分多个中介者
- 层次结构:建立中介者层级(如全局中介者+局部中介者)
- 部分解耦:允许某些对象直接通信
5.3 测试策略
测试中介者模式系统时要注意:
- 单元测试:单独测试每个同事类,用Mock中介者
- 集成测试:测试中介者与同事类的协作
- 性能测试:验证中介者的吞吐量和延迟
示例测试代码:
java复制@Test
void testMediatorRelay() {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleagueA colleagueA = new ConcreteColleagueA();
ConcreteColleagueB colleagueB = new ConcreteColleagueB();
mediator.register(colleagueA);
mediator.register(colleagueB);
// 验证消息中继
colleagueA.send("Test message");
// 断言colleagueB收到了消息
}
6. 中介者模式在现代Java框架中的应用
6.1 Spring框架中的中介者模式
Spring框架的ApplicationContext本质上是一个中介者,管理着所有Bean的创建和依赖注入。例如:
java复制@Service
class OrderService {
@Autowired // 通过Spring中介者获取依赖
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
}
6.2 JMS消息队列作为中介者
Java消息服务(JMS)是中介者模式的典型实现,生产者消费者通过消息队列中介通信:
java复制// 生产者
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("orders");
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("New order"));
// 消费者
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(message -> {
// 处理消息
});
6.3 事件驱动架构中的中介者
现代Java框架如Vert.x、Akka都内置了事件总线,是中介者模式的高级实现:
java复制// Vert.x事件总线示例
EventBus eventBus = vertx.eventBus();
// 发布消息
eventBus.publish("news.feed", "Breaking news!");
// 订阅消息
eventBus.consumer("news.feed", message -> {
System.out.println("Received news: " + message.body());
});
7. 从设计模式到架构模式:中介者的演进
中介者概念不仅存在于设计模式层面,在架构层面也有广泛应用:
- API网关:微服务架构中的请求路由和聚合
- 服务网格:服务间通信的基础设施层
- EDA架构:事件驱动架构中的事件总线
- CQRS模式:命令和查询的分离处理
这些架构模式都可以视为中介者模式在更大尺度上的应用。理解基础的设计模式,有助于我们更好地掌握这些复杂架构。
在实现复杂系统时,我通常会先从小规模的中介者开始,随着系统演进逐步扩展。关键是要保持中介者的单一职责,避免让它变成"上帝对象"。当发现中介者类超过1000行代码时,就是考虑拆分的好时机。
