中介者模式:对象交互解耦的艺术
说实话,我在刚做架构设计那几年,最头疼的就是对象之间的通信问题。你想想看,一个系统里十几个类互相调用,A调B,B调C,C又调A……最后代码改一处,牵一发而动全身。这种场景,我猜你也遇到过。
中介者模式(Mediator Pattern)就是专门解决这个问题的。它把对象之间的网状交互,变成了星型交互——所有对象只跟中介者说话,中介者负责转发消息。说白了,就是给这群对象请了个“管家”。
为什么需要中介者?
先看一个反面教材。假设你写一个聊天室,没有中介者的话,每个用户都要知道其他所有用户的引用:
// 糟糕的设计:用户之间直接通信
public class User {
private String name;
private List<User> friends;
public void sendMessage(String msg, User to) {
to.receive(msg, this);
}
public void receive(String msg, User from) {
System.out.println(from.name + " 对你说: " + msg);
}
}
这有什么问题?耦合太紧。每个用户都要维护一个好友列表,新增一个用户,所有相关用户都得更新。更可怕的是,如果我想加个“群发”功能,每个用户都得改代码。
我在一个即时通讯项目里就吃过这个亏。当时团队图省事,让客户端之间直接通信。结果上线后,加一个“消息已读”功能,改了十几个文件……后来我果断重构,引入了中介者模式。
中介者模式的核心结构
先看一张图,帮你快速理解:
这张图很直观:同事对象(Colleague)只跟中介者(Mediator)交互。中介者知道所有同事,但同事之间互不知道。这就是解耦的关键。
Java实现:聊天室案例
咱们用聊天室来实战。先定义中介者接口:
// 中介者接口
public interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}
然后实现具体的中介者——聊天室:
public class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
users.add(user);
}
@Override
public void sendMessage(String msg, User sender) {
for (User user : users) {
// 消息不发给发送者自己
if (user != sender) {
user.receive(msg);
}
}
}
}
再定义用户类(同事对象):
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 msg);
public abstract void receive(String msg);
}
public class ChatUser extends User {
public ChatUser(String name, ChatMediator mediator) {
super(name, mediator);
}
@Override
public void send(String msg) {
System.out.println(name + " 发送: " + msg);
mediator.sendMessage(msg, this);
}
@Override
public void receive(String msg) {
System.out.println(name + " 收到: " + msg);
}
}
客户端调用:
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("大家好!");
// 输出:
// Alice 发送: 大家好!
// Bob 收到: 大家好!
// Charlie 收到: 大家好!
关键点:Alice 发送消息时,她根本不知道 Bob 和 Charlie 的存在。她只跟 chatRoom 打交道。如果后来加了新用户,Alice 的代码一行都不用改。
C++实现:同样的思路
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 前向声明
class User;
// 中介者接口
class ChatMediator {
public:
virtual void sendMessage(const string& msg, User* sender) = 0;
virtual void addUser(User* user) = 0;
virtual ~ChatMediator() {}
};
// 同事基类
class User {
protected:
string name;
ChatMediator* mediator;
public:
User(const string& n, ChatMediator* m) : name(n), mediator(m) {}
virtual void send(const string& msg) = 0;
virtual void receive(const string& msg) = 0;
virtual ~User() {}
string getName() const { return name; }
};
// 具体中介者
class ChatRoom : public ChatMediator {
private:
vector<User*> users;
public:
void addUser(User* user) override {
users.push_back(user);
}
void sendMessage(const string& msg, User* sender) override {
for (User* user : users) {
if (user != sender) {
user->receive(msg);
}
}
}
};
// 具体用户
class ChatUser : public User {
public:
ChatUser(const string& n, ChatMediator* m) : User(n, m) {}
void send(const string& msg) override {
cout << name << " 发送: " << msg << endl;
mediator->sendMessage(msg, this);
}
void receive(const string& msg) override {
cout << name << " 收到: " << msg << endl;
}
};
// 使用示例
int main() {
ChatRoom chatRoom;
ChatUser alice("Alice", &chatRoom);
ChatUser bob("Bob", &chatRoom);
ChatUser charlie("Charlie", &chatRoom);
chatRoom.addUser(&alice);
chatRoom.addUser(&bob);
chatRoom.addUser(&charlie);
alice.send("大家好!");
return 0;
}
中介者模式在MVC中的应用
说到这个,我不得不提MVC架构。你仔细想想,MVC里的Controller其实就是个中介者。
| MVC组件 | 对应中介者模式 | 职责 |
|---|---|---|
| View(视图) | 同事对象 | 展示数据,接收用户输入 |
| Model(模型) | 同事对象 | 管理数据,业务逻辑 |
| Controller(控制器) | 中介者 | 协调View和Model的交互 |
View不直接操作Model,Model也不直接更新View。所有交互都通过Controller转发。这就是中介者模式在大型框架中的经典应用。
我的经验:在Spring MVC中,DispatcherServlet就是一个全局中介者。它接收所有HTTP请求,然后分发给对应的Controller处理。Controller再调用Service(Model层),最后返回ModelAndView给DispatcherServlet,由它决定渲染哪个View。
避坑指南
我曾经踩过的坑:中介者模式虽然解耦了同事对象,但中介者本身会变得非常复杂。如果你把所有逻辑都塞进中介者,它就成了一个“上帝对象”——什么都知道,什么都管。这会导致中介者难以维护。
怎么避免?我建议:
- 职责单一:一个中介者只负责一类交互。比如聊天室中介者只管消息转发,不要让它管用户认证、文件传输。
- 考虑事件驱动:如果交互逻辑太复杂,可以用事件总线(Event Bus)替代中介者。事件总线本质上也是一种中介者,但更灵活。
- 不要滥用:如果只有两三个对象交互,直接通信反而更简单。中介者模式适合“多对多”的复杂场景。
什么时候用中介者模式?
我总结了几条判断标准:
- 对象之间的交互关系复杂,形成网状结构
- 多个对象需要互相通信,但你想让它们互相不知道对方
- 你想把交互逻辑集中管理,方便后续修改
- 系统需要支持动态增加或移除参与者
举个例子,我在做一个物联网平台时,设备、传感器、报警器、日志系统之间需要频繁通信。用中介者模式后,新增一种设备类型,只需要改中介者,其他设备完全不受影响。这就是解耦带来的好处。
嗯,中介者模式就聊到这儿。记住一句话:把复杂交互交给中介者,让每个对象只关心自己的事。这样你的代码会清爽很多。