中介者模式:对象间解耦、集中控制、实战:聊天室系统
说实话,中介者模式是我在重构老系统时用得最多的模式之一。为什么?因为很多项目写着写着,对象之间的引用关系就乱成一锅粥了。你想想看,A 调 B,B 调 C,C 又调 A……这种网状结构,改一个地方,牵一发动全身。
中介者模式就是来解决这个问题的。它把多对多的交互,转成了一对多的控制。说白了,就是找个“中间人”来统一调度。
什么是中介者模式?
中介者模式(Mediator Pattern)定义了一个中介对象,用来封装一组对象之间的交互。这些对象不需要直接引用对方,只需要跟中介者打交道就行。
我个人的理解是:它就像公司里的项目经理。开发人员不用直接去找产品经理吵架,也不用直接去跟测试对线。所有需求、问题、变更,都通过项目经理来协调。项目经理就是那个中介者。
核心思想: 将对象间的多对多关系,转化为中介者与各对象的一对多关系。
为什么需要中介者模式?
我在项目中遇到过这样一个场景:一个电商后台系统,订单模块、库存模块、物流模块、通知模块之间互相调用。订单创建后要通知库存扣减,库存扣完要通知物流发货,物流发货后要通知用户……
每个模块都持有其他模块的引用。后来需求变了,要加一个优惠券模块。好嘛,所有相关模块都得改一遍。这就是典型的“网状结构”带来的维护噩梦。
中介者模式能带来两个明显的好处:
- 解耦: 各对象不再直接引用,只依赖中介者
- 集中控制: 交互逻辑集中在中介者里,改一处就行
模式结构
中介者模式涉及以下几个角色:
| 角色 | 说明 |
|---|---|
| Mediator(抽象中介者) | 定义通信接口,声明消息发送方法 |
| ConcreteMediator(具体中介者) | 实现协调逻辑,维护各同事对象的引用 |
| Colleague(同事类) | 每个同事对象都持有中介者的引用,通过中介者与其他同事通信 |
嗯,这里要注意:同事类之间不能直接调用。所有通信必须走中介者。这是原则,破了这个原则,中介者模式就白搭了。
SVG 结构图:中介者模式核心逻辑
实战:聊天室系统
聊天室是中介者模式的经典案例。每个用户(同事)发送消息时,不是直接发给其他用户,而是发给聊天室服务器(中介者),由服务器转发给所有人。
我们来实现一个简化版。
1. 定义抽象中介者
public abstract class ChatMediator {
public abstract void sendMessage(String message, User user);
public abstract void addUser(User user);
}
2. 定义同事类(用户)
public abstract class User {
protected String name;
protected ChatMediator mediator;
public User(String name, ChatMediator mediator) {
this.name = name;
this.mediator = mediator;
}
public abstract void send(String message);
public abstract void receive(String message);
}
3. 实现具体中介者
public class ChatRoom extends ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
users.add(user);
}
@Override
public void sendMessage(String message, User sender) {
for (User user : users) {
// 消息不发给发送者自己
if (user != sender) {
user.receive(sender.name + ": " + message);
}
}
}
}
4. 实现具体用户
public class ChatUser extends User {
public ChatUser(String name, ChatMediator mediator) {
super(name, mediator);
}
@Override
public void send(String message) {
System.out.println(this.name + " 发送消息: " + message);
mediator.sendMessage(message, this);
}
@Override
public void receive(String message) {
System.out.println(this.name + " 收到消息: " + message);
}
}
5. 客户端使用
public class Client {
public static void main(String[] args) {
ChatMediator chatRoom = new ChatRoom();
User alice = new ChatUser("Alice", chatRoom);
User bob = new ChatUser("Bob", chatRoom);
User charlie = new ChatUser("Charlie", chatRoom);
chatRoom.addUser(alice);
chatRoom.addUser(bob);
chatRoom.addUser(charlie);
alice.send("大家好!");
bob.send("嗨,Alice!");
}
}
运行结果:
Alice 发送消息: 大家好!
Bob 收到消息: Alice: 大家好!
Charlie 收到消息: Alice: 大家好!
Bob 发送消息: 嗨,Alice!
Alice 收到消息: Bob: 嗨,Alice!
Charlie 收到消息: Bob: 嗨,Alice!
个人经验: 我在做即时通讯项目时,一开始没考虑中介者模式,每个客户端直接维护其他客户端的连接列表。后来用户量一上来,连接管理、消息转发、异常处理全混在一起,代码根本没法维护。重构后用了类似上面的中介者结构,问题迎刃而解。
中介者模式的优缺点
| 优点 | 缺点 |
|---|---|
| 减少类之间的直接耦合 | 中介者可能变得庞大复杂 |
| 将多对多简化为一对多 | 中介者出问题会影响所有同事 |
| 交互逻辑集中,便于维护 | 同事类数量多时,中介者逻辑膨胀 |
| 新增同事类只需修改中介者 | 可能引入性能瓶颈 |
避坑指南: 我曾经在一个项目中把中介者写成了“上帝对象”——所有业务逻辑都往里塞。结果中介者类超过 3000 行,改一个功能要翻半天代码。记住:中介者只负责协调,不负责业务处理。业务逻辑应该留在各自的同事类里。
什么时候用中介者模式?
我个人习惯在以下几种场景考虑使用:
- 一组对象之间通信方式复杂,形成了网状结构
- 想复用某个对象,但它依赖太多其他对象
- 交互逻辑经常变化,希望集中管理
但也要注意,如果对象之间的交互很简单,比如就两三个对象互相调用,那硬套中介者模式反而画蛇添足。设计模式是工具,不是目的。
小结
中介者模式的核心就一句话:让对象之间不直接说话,都通过中介者传话。它把混乱的网状结构,变成了清晰的一对多结构。
聊天室系统只是其中一个例子。你想想看,GUI 里的对话框、MVC 中的 Controller、消息队列中的 Broker……本质上都是中介者模式的变体。理解了这一点,你会发现这个模式其实无处不在。
嗯,最后提醒一句:别把中介者写成大泥球。保持它的职责单一,只做协调,不做业务。这样你的代码才能既优雅又健壮。
公众号:蓝海资料掘金营,微信deep3321