前言
在软件开发中,我们经常遇到对象之间存在复杂交互关系的场景。当多个对象相互依赖、相互调用时,代码会变得错综复杂,维护成本急剧上升。这种情况下,**中介者模式(Mediator Pattern)**应运而生。
今天,我们就来深入探讨这个能够有效解决对象间过度耦合的设计模式。
一、中介者模式概述
1.1 什么是中介者模式?
**中介者模式**是一种行为型设计模式,它定义了一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。
1.2 生活中的例子
想象一下机场塔台的工作场景:
- 多架飞机(同事对象)需要起飞、降落、飞行
- 如果飞机之间直接通信,需要知道其他所有飞机的位置和状态,这会造成混乱
- 有了塔台(中介者)后,所有飞机只与塔台通信,由塔台协调调度
这就是中介者模式的精髓:通过一个中间人来管理复杂的关系网络。
1.3 核心思想
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
二、中介者模式的结构
2.1 角色划分
中介者模式包含以下四个核心角色:
┌─────────────────────────────────────────┐
│ Mediator(抽象中介者) │
│ + defineColleague() │
│ + operation() │
└─────────────────────────────────────────┘
▲
│ 继承/实现
│
┌─────────────────────────────────────────┐
│ ConcreteMediator(具体中介者) │
│ - colleagueList: List<Colleague> │
│ + addColleague() │
│ + operation() │
└─────────────────────────────────────────┘
│ 管理 │ 管理
│ │
┌──────────────┐ ┌──────────────┐
│ Colleague A │ │ Colleague B │
│ (同事对象) │ │ (同事对象) │
└──────────────┘ └──────────────┘
各角色职责:
- Mediator(抽象中介者):定义同事对象到中介者对象的接口
- ConcreteMediator(具体中介者):实现抽象中介者的方法,协调各同事对象之间的交互
- Colleague(抽象同事类):定义同事类的接口,保存中介者对象
- ConcreteColleague(具体同事类):实现抽象同事类的方法,需要与其他同事交互时,通过中介者完成
三、中介者模式的代码实现
3.1 场景设定
以智能家电控制系统为例:
- 空调、电视、灯光、窗帘等家电
- 打开电视时,灯光自动调暗
- 开启空调时,窗帘自动关闭
- 关闭所有家电时,一键操作
3.2 抽象中介者接口
java
/**
* 抽象中介者接口
*/
public interface Mediator {
/**
* 注册同事对象
*/
void register(Colleague colleague);
/**
* 转发消息,协调各同事对象之间的交互
*/
void operation(String message, Colleague colleague);
}
3.3 抽象同事类
java
/**
* 抽象同事类
*/
public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
// 将自己注册到中介者
mediator.register(this);
}
/**
* 发送消息,通过中介者转发
*/
public void send(String message) {
mediator.operation(message, this);
}
/**
* 接收消息,由子类实现具体逻辑
*/
public abstract void receive(String message);
}
3.4 具体同事类
java
/**
* 空调
*/
public class AirConditioner extends Colleague {
public AirConditioner(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("【空调】收到消息:" + message);
if ("电视已开启".equals(message)) {
System.out.println("【空调】响应:调整温度到舒适模式");
}
}
public void turnOn() {
System.out.println("【空调】已开启");
send("空调已开启");
}
}
/**
* 电视
*/
public class Television extends Colleague {
public Television(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("【电视】收到消息:" + message);
if ("空调已开启".equals(message)) {
System.out.println("【电视】响应:调低音量");
}
}
public void turnOn() {
System.out.println("【电视】已开启");
send("电视已开启");
}
}
/**
* 灯光
*/
public class Light extends Colleague {
public Light(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("【灯光】收到消息:" + message);
if ("电视已开启".equals(message)) {
System.out.println("【灯光】响应:调暗灯光");
} else if ("空调已开启".equals(message)) {
System.out.println("【灯光】响应:调整为暖色调");
}
}
}
3.5 具体中介者
java
import java.util.ArrayList;
import java.util.List;
/**
* 智能家居中介者
*/
public class SmartHomeMediator implements Mediator {
private List<Colleague> colleagues = new ArrayList<>();
@Override
public void register(Colleague colleague) {
colleagues.add(colleague);
}
@Override
public void operation(String message, Colleague sender) {
// 中介者协调各同事对象之间的交互
for (Colleague colleague : colleagues) {
// 不需要发送给自己
if (colleague != sender) {
colleague.receive(message);
}
}
}
}
3.6 客户端调用
java
public class Client {
public static void main(String[] args) {
System.out.println("========== 中介者模式测试 ==========\n");
// 创建中介者
Mediator mediator = new SmartHomeMediator();
// 创建同事对象,并将中介者传入
AirConditioner ac = new AirConditioner(mediator);
Television tv = new Television(mediator);
Light light = new Light(mediator);
// 测试:打开电视
System.out.println("--- 场景1:打开电视 ---");
tv.turnOn();
System.out.println("\n--- 场景2:打开空调 ---");
ac.turnOn();
System.out.println("\n========== 测试结束 ==========");
}
}
3.7 运行结果
========== 中介者模式测试 ==========
--- 场景1:打开电视 ---
【电视】已开启
【空调】收到消息:电视已开启
【空调】响应:调整温度到舒适模式
【灯光】收到消息:电视已开启
【灯光】响应:调暗灯光
--- 场景2:打开空调 ---
【空调】已开启
【电视】收到消息:空调已开启
【电视】响应:调低音量
【灯光】收到消息:空调已开启
【灯光】响应:调整为暖色调
========== 测试结束 ==========
四、中介者模式的优缺点
4.1 优点
| 优点 | 说明 |
|---|---|
| 降低耦合度 | 对象之间不需要显式引用,通过中介者通信 |
| 集中控制交互 | 将复杂的网状结构变为星形结构,易于管理 |
| 符合开闭原则 | 新增同事类不影响原有代码,只需扩展中介者 |
| 简化对象协议 | 对象之间的一对多关系转换为一对一关系 |
4.2 缺点
| 缺点 | 说明 |
|---|---|
| 中介者职责过重 | 随着同事类增多,中介者会变得复杂庞大 |
| 难以维护 | 中介者逻辑复杂时,不利于后续维护和扩展 |
| 性能开销 | 所有交互都通过中介者转发,可能影响性能 |
五、中介者模式的应用场景
5.1 适用场景
✅ 对象之间存在复杂的网状结构
当多个对象之间存在多对多的交互关系时
✅ 希望降低对象之间的耦合度
不希望对象之间显式地相互引用
✅ 需要统一管理和协调交互
需要一个中心控制器来协调多个对象的行为
5.2 实际应用案例
-
MVC框架中的控制器(Controller)
- View和Model之间通过Controller交互
-
GUI框架中的事件监听机制
- Swing/AWT中的事件分发器
-
消息中间件
- Kafka、RabbitMQ等消息队列系统
-
聊天室系统
- 用户之间通过聊天服务器转发消息
六、中介者模式与其他模式的对比
6.1 中介者模式 vs 观察者模式
| 对比维度 | 中介者模式 | 观察者模式 |
|---|---|---|
| 目的 | 解耦对象之间的复杂交互 | 一对多的依赖关系,状态变化通知 |
| 结构 | 星形结构,中心化 | 树形/网形结构,去中心化 |
| 控制权 | 中介者集中控制 | 主题对象广播,观察者自主响应 |
6.2 中介者模式 vs 门面模式
| 对比维度 | 中介者模式 | 门面模式 |
|---|---|---|
| 目的 | 解耦对象间的交互 | 简化外部系统的调用 |
| 交互 | 对象之间双向交互 | 单向调用,外部→内部 |
| 修改性 | 对象之间可动态交互 | 外部不感知内部变化 |
七、总结
7.1 核心要点回顾
- 中介者模式通过引入中介者对象,将复杂的网状结构变为简单的星形结构
- 适用场景:对象间存在复杂交互、需要降低耦合度、需要集中控制
- 注意点:避免中介者成为"上帝类",合理控制其职责范围
7.2 最佳实践建议
- 适度使用:只有在对象间关系确实复杂时才引入中介者
- 职责分离:可以设计多个专门的中介者,避免单一中介者过于庞大
- 结合其他模式:可以与观察者模式、工厂模式等结合使用
7.3 最后的话
中介者模式就像一个优秀的项目经理,他不直接参与具体工作,但通过协调各方资源,确保整个项目顺利进行。在合适的场景下使用,能让代码更加清晰、可维护。