中介者模式(Mediator Pattern)是行为型设计模式之一,它的核心思想是引入一个中介者对象来封装一系列对象之间的交互。
简单来说,就是让原本杂乱无章的"网状结构"通信,变成井井有条的"星型结构"通信。这就好比现实中的房产中介:房主不需要直接认识租客,租客也不需要直接认识房主,他们只需要通过中介来传递信息即可。
🎯 核心角色
中介者模式主要包含以下四个核心角色:
抽象中介者(Mediator):定义了同事对象用来通信的接口(如 sendMessage)。
具体中介者(ConcreteMediator):实现了中介者接口。它知道所有的具体同事对象,并负责协调各个同事对象之间的交互逻辑。
抽象同事类(Colleague):定义了同事类的接口,并持有一个对中介者对象的引用。
具体同事类(ConcreteColleague):实现了抽象同事类。它不再直接与其他同事通信,而是通过中介者来完成交互。
📐 结构演变:从网状到星型
没有使用中介者时(网状结构):假设有 N 个组件,如果它们需要两两通信,依赖关系会呈指数级增长(复杂度 O(N2)O(N2) )。改一处代码可能牵动全身。
使用中介者后(星型结构):所有组件只和"中介者"通信。组件之间不再有直接依赖,新增或修改组件不会影响其他组件,复杂度降至 O(N)O(N) 。
💻 代码实现示例(聊天室场景)
我们模拟一个简单的聊天室,用户之间不直接发消息,而是通过聊天室(中介者)来转发。
- 定义抽象中介者和具体中介者
java
import java.util.*;
// 抽象中介者
interface ChatMediator {
void addUser(User user);
void sendMessage(String message, User sender);
}
// 具体中介者:聊天室
class ChatRoom implements 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(message);
}
}
}
}
- 定义同事类
java
// 抽象同事类
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);
}
// 具体同事类
class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
mediator.addUser(this); // 注册到中介者
}
@Override
public void send(String message) {
System.out.println(name + " 发送: " + message);
mediator.sendMessage(message, this); // 通过中介者发送
}
@Override
public void receive(String message) {
System.out.println(name + " 收到: " + message);
}
}
- 客户端测试
java
public class MediatorDemo {
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");
// Alice 发送消息,Bob 和 Charlie 会收到
alice.send("大家好!");
}
}
输出结果:
Alice 发送: 大家好!
Bob 收到: 大家好!
Charlie 收到: 大家好!
✅ 优缺点分析
优点:
降低耦合度:同事对象之间不再相互引用,只依赖中介者,符合"迪米特法则"(最少知道原则)。
易于维护和扩展:交互逻辑集中在中介者中。如果要修改交互规则,只需修改中介者,而无需改动各个同事类。
复用性高:同事对象可以独立变化,更容易被复用。
缺点:
中介者可能过于复杂:如果系统中同事类太多,或者交互逻辑极其复杂,中介者会变成一个包含大量逻辑的"上帝类",难以维护。
可能成为性能瓶颈:所有消息都经过中介者转发,如果中介者处理能力不足,会影响整体性能。
🚀 适用场景
中介者模式(Mediator Pattern)的核心价值在于解耦。当系统中对象之间的交互关系变得复杂、呈现出"网状结构"时,就是中介者模式大显身手的时候。简单来说,它的使用场景通常符合这样一个特征:多个对象之间需要相互通信,且这种通信关系经常变化或难以维护。
结合我搜索到的信息,以下是中介者模式最典型的具体应用场景:
1. 图形用户界面(GUI)组件交互
这是教科书级的应用场景。
场景描述:在一个复杂的表单或对话框中,多个组件(如按钮、文本框、下拉列表、复选框)的状态是相互关联的。
在一个登录窗口中,只有当"用户名"和"密码"输入框都不为空时,"登录"按钮才变为可用状态。
勾选"全选"复选框时,下面所有的子选项都要被选中。
为什么用中介者:如果不使用中介者,按钮就需要持有对文本框的引用,文本框也需要监听按钮的状态,导致类之间互相依赖,代码极其混乱。通过引入"窗口对象"作为中介者,所有组件只需向窗口报告事件,由窗口统一判断并更新其他组件的状态。
2. 聊天系统与即时通讯
场景描述:在群聊或多人在线游戏中,用户(或玩家)之间需要发送消息。
具体例子:微信群聊:你发消息给群,群再把消息分发给其他人,你不需要直接连接到每个群成员的设备。游戏中的队伍频道。
为什么用中介者:如果用户之间直接通信(网状连接),新增一个用户需要建立 N 次连接;通过"聊天服务器"或"频道"作为中介者,用户只需连接服务器,由服务器负责转发(星型结构),极大地降低了网络复杂度。
3. 智能家居与物联网(IoT)设备联动
场景描述:家里的灯光、空调、窗帘、音响等设备需要根据场景自动协作。
具体例子:"观影模式":按下遥控器,灯光自动关闭,窗帘拉上,电视和音响开启。"回家模式":门锁打开后,灯自动亮起,空调调到舒适温度。
为什么用中介者:如果让灯直接去控制窗帘,再让窗帘去通知空调,设备之间会高度耦合,无法独立更换设备。通过"智能中枢"(如 HomeAssistant 或手机 App)作为中介者,设备只向中枢发送状态(如"门开了"),由中枢决定调度哪些设备,实现"即插即用"。
4. 微服务与分布式系统协调
场景描述:在复杂的业务流程中,涉及多个微服务(如订单、库存、支付、物流)。
具体例子:电商下单流程:创建订单 -> 扣减库存 -> 发起支付 -> 通知物流。使用消息队列(如 Kafka、RabbitMQ)或服务注册中心(如 Nacos、Eureka)。
为什么用中介者:如果服务之间直接调用(A调B,B调C),一旦某个服务宕机或下线,整个链条都会断裂。通过消息中间件或注册中心作为"中介者",服务之间通过发布/订阅消息进行异步通信,实现了服务解耦和流量削峰。
5. 工作流与审批系统
场景描述:OA 系统、财务报销系统,涉及多角色按规则流转任务。
具体例子:请假流程:员工提交 -> 主管审批 -> HR 备档 -> 财务发薪。采购流程:申请 -> 部门经理 -> 财务审核 -> 总经理审批。
为什么用中介者:如果把审批逻辑硬编码在代码里,每增加一个审批节点就要改代码。通过"流程引擎"作为中介者,将流程定义为配置文件(如 XML 或 JSON),引擎根据配置动态决定下一步该谁审批,实现了逻辑与代码分离。
6. 航空交通管制
场景描述:多架飞机在机场附近飞行。
具体例子:飞机之间不直接协商谁先降落,而是听从塔台的指挥。
为什么用中介者:这是现实世界中最直观的中介者例子。塔台(Mediator)掌握了所有飞机(Colleague)的位置和状态,统一调度以避免碰撞。
7.何时应该使用中介者模式?
你可以通过以下三个"信号"来判断是否需要使用中介者模式:
- 网状依赖:类图中线条交错杂乱,对象之间互相持有引用。
- 修改困难:想修改一个对象的行为,结果发现必须去修改它所依赖的其他好几个对象的代码。
- 复用困难:因为对象依赖太多其他对象,导致你无法单独把这个对象拿出来用在别的地方。
注意:虽然中介者模式很好用,但要避免将其变成"上帝类"(God Class),即中介者承担了过多的职责,导致中介者本身代码过于臃肿难以维护。