中介者模式:让对象间的“多对多”变成“一对多”
说实话,中介者模式是我在实际项目中用得比较多的模式之一。为什么?因为只要系统里对象一多,它们之间的通信就会变得混乱。你想想看,如果每个对象都要知道其他对象的存在,那代码维护起来简直是一场噩梦。
中介者模式的核心思想其实很简单:用一个中介对象来封装一组对象的交互。这些对象不再直接相互引用,而是通过中介者来通信。说白了,就是把“多对多”的关系,简化成“一对多”的关系。
核心定义:中介者模式(Mediator Pattern)定义了一个中介对象来封装一组对象之间的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,并且可以独立地改变它们之间的交互。
为什么需要中介者?
我在一个即时通讯项目里遇到过这样的问题:用户、群组、文件传输、通知服务……这些模块之间互相调用,代码里到处都是引用。后来加一个新功能,改了三个文件才搞定。我当时就想,这肯定得重构。
中介者模式正好解决这类问题。它把对象间的网状结构,变成了星型结构。每个对象只跟中介者打交道,中介者负责协调它们之间的交互。
嗯,这里要注意:中介者模式不是万能的。它适合那些对象间交互复杂、但交互逻辑相对稳定的场景。如果交互逻辑经常变,那中介者本身就会变成一个“上帝对象”,反而更难维护。
模式的结构
中介者模式有四个核心角色:
- 抽象中介者(Mediator):定义通信接口,声明同事对象之间交互的方法。
- 具体中介者(ConcreteMediator):实现中介者接口,协调各个同事对象的交互。它需要知道所有同事对象。
- 抽象同事类(Colleague):定义同事对象的公共接口,持有中介者的引用。
- 具体同事类(ConcreteColleague):实现自己的业务逻辑,当需要与其他同事通信时,通过中介者来完成。
我的习惯:在设计同事类时,我会让它们只关注自己的业务逻辑。通信的事情,全部交给中介者。这样每个同事类的职责就非常清晰,测试起来也方便。
代码示例:聊天室
聊天室是中介者模式最经典的例子。用户不直接给其他用户发消息,而是通过聊天室这个中介者来转发。
先看抽象中介者和抽象同事类:
// 抽象中介者
public abstract class ChatMediator {
public abstract void sendMessage(String message, User user);
public abstract void addUser(User user);
}
// 抽象同事类
public abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String message);
public abstract void receive(String message);
public String getName() {
return name;
}
}
再看具体实现:
// 具体中介者
public class ChatRoom extends ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
users.add(user);
System.out.println(user.getName() + " 加入了聊天室");
}
@Override
public void sendMessage(String message, User sender) {
for (User user : users) {
// 消息不发送给自己
if (user != sender) {
user.receive(sender.getName() + ": " + message);
}
}
}
}
// 具体同事类
public class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
}
@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);
}
}
客户端使用:
public class Client {
public static void main(String[] args) {
ChatMediator chatRoom = new ChatRoom();
User alice = new ChatUser(chatRoom, "Alice");
User bob = new ChatUser(chatRoom, "Bob");
User charlie = new ChatUser(chatRoom, "Charlie");
chatRoom.addUser(alice);
chatRoom.addUser(bob);
chatRoom.addUser(charlie);
alice.send("大家好!");
bob.send("嗨,Alice!");
}
}
运行结果:
Alice 加入了聊天室
Bob 加入了聊天室
Charlie 加入了聊天室
Alice 发送消息: 大家好!
Bob 收到消息: Alice: 大家好!
Charlie 收到消息: Alice: 大家好!
Bob 发送消息: 嗨,Alice!
Alice 收到消息: Bob: 嗨,Alice!
Charlie 收到消息: Bob: 嗨,Alice!
中介者模式与MVC中的Controller
说到中介者模式,我不得不提MVC架构。你仔细想想,MVC里的Controller其实就是一个中介者。
在MVC中:
- View 负责展示数据,它不直接操作Model。
- Model 负责业务数据和逻辑,它不关心谁在展示它。
- Controller 负责协调View和Model的交互。
用户点击按钮 -> View通知Controller -> Controller更新Model -> Model变化后通知View更新。你看,这不就是中介者模式吗?Controller就是那个中介者,View和Model就是同事类。
我曾经踩过的坑:在一个项目中,我把太多逻辑塞进了Controller里。结果Controller变得臃肿不堪,什么都要管。后来我意识到,Controller应该只负责协调,具体的业务逻辑应该放在Model或者Service层。记住:中介者只负责“传话”,不负责“做事”。
中介者模式的优缺点
| 优点 | 缺点 |
|---|---|
| 降低了同事类之间的耦合度 | 中介者可能变得复杂庞大 |
| 将对象间的交互集中管理 | 中介者出问题会影响整个系统 |
| 符合迪米特法则(最少知道原则) | 增加了系统的复杂度 |
| 便于扩展新的同事类 | 交互逻辑变更时需要修改中介者 |
什么时候用中介者模式?
我个人总结了几条经验:
- 对象间引用关系复杂:当多个对象相互引用,形成网状结构时,考虑引入中介者。
- 想复用对象:如果对象需要在不修改代码的情况下被复用到其他场景,中介者模式能帮你解耦。
- 交互逻辑需要集中管理:当对象间的交互逻辑经常变化,或者需要统一控制时,中介者是个好选择。
避坑指南:我曾经在一个项目中过度使用中介者模式,把所有的对象交互都塞进了一个中介者里。结果那个中介者变成了“上帝对象”,维护起来非常痛苦。后来我学会了:如果交互逻辑可以分组,就创建多个中介者,每个中介者负责一组相关的交互。
中介者模式的核心逻辑图
下面这张图展示了中介者模式的核心结构。你看,所有的同事类都只跟中介者通信,彼此之间没有直接联系。
实际项目中的应用
我记得在一个电商系统中,订单、库存、支付、物流这几个模块之间交互非常频繁。一开始它们直接调用对方的方法,代码耦合严重。后来我用中介者模式重构了这部分逻辑,引入了一个订单处理中心(OrderProcessingCenter)作为中介者。
重构之后,每个模块只跟订单处理中心通信。新增一个优惠券模块时,我只需要修改订单处理中心,其他模块完全不用动。这就是中介者模式的价值所在。
总结一下:中介者模式的核心是“解耦”。它让对象间的交互变得清晰可控。但要注意,不要滥用。如果对象间的交互很简单,直接用引用反而更直接。模式是工具,不是目的。