一、模式定义与核心思想
中介者模式是一种行为设计模式,通过引入一个中介者对象来封装一系列对象之间的交互,使这些对象不需要显式地相互引用,从而降低耦合度,并可以独立地改变它们之间的交互。
核心思想:用中介者对象替代对象间的直接交互
- 从"多对多"交互变为"一对多"交互
- 中介者协调对象间的通信
- 对象间不直接知晓彼此,只与中介者通信
二、Java代码实现
2.1 基础实现:聊天室系统
java
java
下载
复制
// 1. 中介者接口
public interface ChatMediator {
void sendMessage(String message, User user);
void addUser(User user);
void removeUser(User user);
}
// 2. 具体中介者:聊天室
public class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
private String roomName;
public ChatRoom(String roomName) {
this.roomName = roomName;
}
@Override
public void sendMessage(String message, User sender) {
System.out.println("【" + roomName + "】" + sender.getName() + " 发送消息: " + message);
// 广播消息给所有用户(除了发送者)
for (User user : users) {
if (user != sender) {
user.receive(message, sender);
}
}
// 记录聊天历史
logMessage(sender.getName(), message);
}
@Override
public void addUser(User user) {
if (!users.contains(user)) {
users.add(user);
user.setMediator(this);
System.out.println(user.getName() + " 加入了聊天室");
}
}
@Override
public void removeUser(User user) {
users.remove(user);
System.out.println(user.getName() + " 离开了聊天室");
}
private void logMessage(String sender, String message) {
// 模拟记录聊天历史
System.out.println("日志: " + sender + " -> " + message.substring(0, Math.min(message.length(), 20)) + "...");
}
public List<User> getOnlineUsers() {
return new ArrayList<>(users);
}
}
// 3. 同事类抽象
public abstract class User {
protected ChatMediator mediator;
protected String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setMediator(ChatMediator mediator) {
this.mediator = mediator;
}
public abstract void send(String message);
public abstract void receive(String message, User sender);
}
// 4. 具体同事类:普通用户
public class RegularUser extends User {
public RegularUser(String name) {
super(name);
}
@Override
public void send(String message) {
if (mediator != null) {
mediator.sendMessage(message, this);
} else {
System.out.println(name + " 错误: 未加入任何聊天室");
}
}
@Override
public void receive(String message, User sender) {
System.out.println(name + " 收到来自 " + sender.getName() + " 的消息: " + message);
}
}
// 5. 具体同事类:管理员
public class AdminUser extends User {
public AdminUser(String name) {
super(name);
}
@Override
public void send(String message) {
if (mediator != null) {
String adminMessage = "[管理员] " + message;
mediator.sendMessage(adminMessage, this);
}
}
@Override
public void receive(String message, User sender) {
System.out.println("[管理员] " + name + " 收到来自 " + sender.getName() + " 的消息: " + message);
// 管理员特殊处理:监控关键字
if (containsSensitiveWords(message)) {
System.out.println("[警告] 检测到敏感内容,已记录");
}
}
private boolean containsSensitiveWords(String message) {
String[] sensitiveWords = {"攻击", "诈骗", "非法"};
for (String word : sensitiveWords) {
if (message.contains(word)) {
return true;
}
}
return false;
}
}
// 6. 客户端使用
public class ChatDemo {
public static void main(String[] args) {
// 创建中介者
ChatMediator chatRoom = new ChatRoom("技术交流群");
// 创建用户
User alice = new RegularUser("Alice");
User bob = new RegularUser("Bob");
User charlie = new RegularUser("Charlie");
User admin = new AdminUser("系统管理员");
// 用户加入聊天室
chatRoom.addUser(alice);
chatRoom.addUser(bob);
chatRoom.addUser(charlie);
chatRoom.addUser(admin);
System.out.println("\n--- 聊天开始 ---");
// 用户间通过中介者通信
alice.send("大家好,今天我们来讨论设计模式");
bob.send("中介者模式看起来不错");
admin.send("请注意聊天规范");
charlie.send("这个模式能防止诈骗吗?");
// 用户离开
chatRoom.removeUser(bob);
alice.send("Bob离开了,我们继续");
}
}
2.2 进阶示例:飞机空中管制系统
java
java
下载
复制
// 1. 飞机接口
public interface Aircraft {
String getFlightNumber();
int getAltitude();
int getSpeed();
Position getPosition();
void updatePosition(Position newPosition);
void receiveMessage(String message, AirTrafficControl atc);
void sendMessage(String message);
}
// 2. 位置类
public class Position {
private double latitude;
private double longitude;
private int altitude;
public Position(double latitude, double longitude, int altitude) {
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
public double distanceTo(Position other) {
// 简化计算,实际应使用haversine公式
double latDiff = Math.abs(latitude - other.latitude);
double lonDiff = Math.abs(longitude - other.longitude);
return Math.sqrt(latDiff * latDiff + lonDiff * lonDiff);
}
}
// 3. 具体飞机类
public class Airplane implements Aircraft {
private String flightNumber;
private int altitude;
private int speed;
private Position position;
private AirTrafficControl atc;
public Airplane(String flightNumber, Position initialPosition) {
this.flightNumber = flightNumber;
this.position = initialPosition;
this.altitude = initialPosition.getAltitude();
}
public void setATC(AirTrafficControl atc) {
this.atc = atc;
}
@Override
public String getFlightNumber() {
return flightNumber;
}
@Override
public int getAltitude() {
return altitude;
}
@Override
public int getSpeed() {
return speed;
}
@Override
public Position getPosition() {
return position;
}
@Override
public void updatePosition(Position newPosition) {
this.position = newPosition;
this.altitude = newPosition.getAltitude();
System.out.println(flightNumber + " 更新位置: 高度 " + altitude + "米");
}
@Override
public void receiveMessage(String message, AirTrafficControl atc) {
System.out.println(flightNumber + " 收到塔台指令: " + message);
}
@Override
public void sendMessage(String message) {
if (atc != null) {
atc.relayMessage(message, this);
}
}
public void requestAltitudeChange(int newAltitude) {
sendMessage("请求爬升到 " + newAltitude + " 米");
}
}
// 4. 空中交通管制接口
public interface AirTrafficControl {
void registerAircraft(Aircraft aircraft);
void unregisterAircraft(Aircraft aircraft);
void relayMessage(String message, Aircraft sender);
void detectAndResolveConflicts();
void requestAltitudeChange(Aircraft aircraft, int newAltitude);
}
// 5. 具体管制中心
public class ControlTower implements AirTrafficControl {
private List<Aircraft> aircrafts = new ArrayList<>();
private final int MIN_SEPARATION = 1000; // 最小间隔1000米
@Override
public void registerAircraft(Aircraft aircraft) {
if (!aircrafts.contains(aircraft)) {
aircrafts.add(aircraft);
((Airplane) aircraft).setATC(this);
System.out.println("塔台: " + aircraft.getFlightNumber() + " 已注册");
}
}
@Override
public void unregisterAircraft(Aircraft aircraft) {
aircrafts.remove(aircraft);
System.out.println("塔台: " + aircraft.getFlightNumber() + " 已离开管制区");
}
@Override
public void relayMessage(String message, Aircraft sender) {
System.out.println("塔台转发消息 [" + sender.getFlightNumber() + "]: " + message);
// 广播给相关飞机
for (Aircraft aircraft : aircrafts) {
if (aircraft != sender) {
// 只通知附近飞机
if (isNearby(sender, aircraft)) {
aircraft.receiveMessage("附近飞机消息: " + message, this);
}
}
}
}
@Override
public void detectAndResolveConflicts() {
for (int i = 0; i < aircrafts.size(); i++) {
for (int j = i + 1; j < aircrafts.size(); j++) {
Aircraft a1 = aircrafts.get(i);
Aircraft a2 = aircrafts.get(j);
if (isConflict(a1, a2)) {
System.out.println("⚠️ 冲突警报: " + a1.getFlightNumber() + " 和 " + a2.getFlightNumber());
resolveConflict(a1, a2);
}
}
}
}
@Override
public void requestAltitudeChange(Aircraft aircraft, int newAltitude) {
// 检查新高度是否安全
if (isAltitudeSafe(aircraft, newAltitude)) {
System.out.println("塔台批准: " + aircraft.getFlightNumber() + " 爬升到 " + newAltitude + " 米");
// 实际应发送指令给飞机
} else {
System.out.println("塔台拒绝: 高度 " + newAltitude + " 米不安全");
}
}
private boolean isNearby(Aircraft a1, Aircraft a2) {
double distance = a1.getPosition().distanceTo(a2.getPosition());
return distance < MIN_SEPARATION * 2;
}
private boolean isConflict(Aircraft a1, Aircraft a2) {
double distance = a1.getPosition().distanceTo(a2.getPosition());
int altitudeDiff = Math.abs(a1.getAltitude() - a2.getAltitude());
return distance < MIN_SEPARATION && altitudeDiff < 300;
}
private boolean isAltitudeSafe(Aircraft requester, int newAltitude) {
for (Aircraft other : aircrafts) {
if (other != requester) {
int altitudeDiff = Math.abs(newAltitude - other.getAltitude());
double distance = requester.getPosition().distanceTo(other.getPosition());
if (altitudeDiff < 300 && distance < MIN_SEPARATION) {
return false;
}
}
}
return true;
}
private void resolveConflict(Aircraft a1, Aircraft a2) {
// 简单解决策略:让高度较低的飞机爬升
if (a1.getAltitude() < a2.getAltitude()) {
System.out.println("指令: " + a1.getFlightNumber() + " 立即爬升300米");
} else {
System.out.println("指令: " + a2.getFlightNumber() + " 立即爬升300米");
}
}
}
// 6. 客户端使用
public class ATCDemo {
public static void main(String[] args) {
// 创建管制中心
AirTrafficControl tower = new ControlTower();
// 创建飞机
Aircraft flight1 = new Airplane("CA123", new Position(39.9, 116.4, 8000));
Aircraft flight2 = new Airplane("MU456", new Position(40.0, 116.5, 8200));
Aircraft flight3 = new Airplane("CZ789", new Position(39.8, 116.3, 8500));
// 注册飞机
tower.registerAircraft(flight1);
tower.registerAircraft(flight2);
tower.registerAircraft(flight3);
// 飞机间通信
flight1.sendMessage("请求航线信息");
// 检测冲突
System.out.println("\n--- 检测冲突 ---");
tower.detectAndResolveConflicts();
// 请求高度变更
System.out.println("\n--- 高度变更请求 ---");
((Airplane) flight1).requestAltitudeChange(9000);
// 再次检测冲突
System.out.println("\n--- 再次检测冲突 ---");
tower.detectAndResolveConflicts();
}
}
三、应用场景
3.1 典型应用场景
-
GUI组件交互
- 复杂表单验证:多个输入框的联动验证
- 组件状态同步:复选框、单选框、按钮的启用/禁用状态
- 对话框组件协调
-
消息系统/聊天应用
- 群聊系统:用户不直接通信,通过聊天室转发
- 消息路由:根据消息类型路由到不同处理程序
- 发布-订阅系统的中心调度
-
空中交通管制/调度系统
- 飞机间的冲突检测与解决
- 资源分配与调度
- 状态同步与通知
-
工作流引擎
- 任务协调与分配
- 状态流转控制
- 参与者间的通信协调
-
微服务架构中的API网关
- 请求路由与聚合
- 服务发现与负载均衡
- 认证授权的统一处理
-
游戏开发
- 游戏实体间的交互协调
- 碰撞检测与处理
- 状态同步与广播
-
企业应用集成
- 系统间数据交换
- 业务流程编排
- 事件驱动架构的事件总线
3.2 使用时机判断
- ✅ 对象间存在复杂的网状引用关系
- ✅ 对象间通信逻辑复杂,难以理解和维护
- ✅ 需要集中控制多个对象间的交互
- ✅ 对象间的依赖关系导致复用困难
- ✅ 需要在运行时动态改变对象间的交互行为
四、优缺点分析
4.1 优点
- 单一职责原则:将对象间复杂的交互逻辑抽取到中介者中
- 开闭原则:新增同事类时无需修改现有同事类
- 降低耦合度:对象间不直接依赖,只依赖中介者
- 简化对象协议:从多对多交互简化为一对多交互
- 集中控制:所有交互逻辑集中在中介者,易于理解和维护
- 减少子类生成:将行为分布在各个同事类之间,而不是通过继承
4.2 缺点
- 中介者可能过于复杂:随着交互逻辑增加,中介者可能成为"上帝对象"
- 性能瓶颈:所有通信都经过中介者,可能成为性能瓶颈
- 单点故障:中介者故障会影响整个系统
- 增加新交互困难:需要修改中介者,可能违反开闭原则
- 调试困难:交互逻辑集中,难以追踪具体交互路径
- 可能引入循环依赖:中介者和同事类相互引用
五、模式变体与改进
5.1 模式变体
java
java
下载
复制
// 变体1:分层中介者
public abstract class AbstractMediator {
protected List<Colleague> colleagues = new ArrayList<>();
public void addColleague(Colleague colleague) {
colleagues.add(colleague);
}
public abstract void mediate(Colleague colleague, String event);
}
// 变体2:可观察的中介者
public class ObservableMediator implements ChatMediator {
private List<ChatObserver> observers = new ArrayList<>();
public void addObserver(ChatObserver observer) {
observers.add(observer);
}
@Override
public void sendMessage(String message, User user) {
// 通知观察者
for (ChatObserver observer : observers) {
observer.onMessageSent(user, message);
}
// 原有发送逻辑...
}
}
// 变体3:多态中介者
public interface PolymorphicMediator {
<T extends Colleague> void mediate(T colleague, String event);
}
// 变体4:事件驱动中介者
public class EventDrivenMediator {
private Map<String, List<EventHandler>> eventHandlers = new HashMap<>();
public void registerHandler(String eventType, EventHandler handler) {
eventHandlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
}
public void fireEvent(String eventType, Event event) {
List<EventHandler> handlers = eventHandlers.get(eventType);
if (handlers != null) {
for (EventHandler handler : handlers) {
handler.handle(event);
}
}
}
}
5.2 结合其他模式
java
java
下载
复制
// 结合观察者模式
public class ObserverMediator {
private Map<Colleague, List<Colleague>> observers = new HashMap<>();
public void notifyColleagues(Colleague source, String event) {
List<Colleague> targets = observers.get(source);
if (targets != null) {
for (Colleague target : targets) {
target.onEvent(event, source);
}
}
}
}
// 结合命令模式
public class CommandMediator {
private Map<String, Command> commandRegistry = new HashMap<>();
public void registerCommand(String commandName, Command command) {
commandRegistry.put(commandName, command);
}
public void executeCommand(String commandName, Colleague invoker) {
Command command = commandRegistry.get(commandName);
if (command != null) {
command.execute(invoker);
}
}
}
// 结合责任链模式
public class ChainMediator {
private HandlerChain handlerChain = new HandlerChain();
public void handleRequest(Colleague colleague, Request request) {
handlerChain.handle(colleague, request);
}
}
六、注意事项与最佳实践
6.1 使用注意事项
-
中介者复杂度控制
java java 下载 复制 // 避免上帝对象 public class ModularMediator { // 将不同领域的交互分离到不同处理器 private ValidationHandler validationHandler; private NotificationHandler notificationHandler; private StateHandler stateHandler; public void mediate(Colleague colleague, String event) { // 根据事件类型路由到不同处理器 switch (event) { case "VALIDATE": validationHandler.handle(colleague); break; case "NOTIFY": notificationHandler.handle(colleague); break; // ... } } } -
避免循环调用
java java 下载 复制 public class SafeMediator { private boolean isMediating = false; public void mediate(Colleague colleague, String event) { if (isMediating) { return; // 防止递归调用 } isMediating = true; try { // 实际的协调逻辑 for (Colleague other : colleagues) { if (other != colleague) { other.receive(event, colleague); } } } finally { isMediating = false; } } } -
线程安全性
java java 下载 复制 public class ThreadSafeMediator { private final List<Colleague> colleagues = Collections.synchronizedList(new ArrayList<>()); private final Object lock = new Object(); public void mediate(Colleague colleague, String event) { synchronized (lock) { // 线程安全的协调逻辑 List<Colleague> copy = new ArrayList<>(colleagues); for (Colleague other : copy) { if (other != colleague) { other.receive(event, colleague); } } } } }
6.2 最佳实践
-
明确中介者职责
- 中介者应专注于协调,不处理业务逻辑
- 业务逻辑应放在同事类中
-
保持同事类独立
- 同事类不应知道其他同事类的存在
- 只通过接口与中介者通信
-
使用接口抽象
java java 下载 复制 public interface IMediator { void send(String message, IColleague sender); } public interface IColleague { void receive(String message, IColleague sender); void setMediator(IMediator mediator); } -
支持动态中介者切换
java java 下载 复制 public class DynamicColleague implements IColleague { private IMediator mediator; @Override public void setMediator(IMediator mediator) { this.mediator = mediator; } public void switchMediator(IMediator newMediator) { this.mediator = newMediator; } } -
实现中介者注册机制
java java 下载 复制 public class RegistryMediator { private Map<String, IColleague> registry = new ConcurrentHashMap<>(); public void register(String id, IColleague colleague) { registry.put(id, colleague); colleague.setMediator(this); } public void sendTo(String targetId, String message, IColleague sender) { IColleague target = registry.get(targetId); if (target != null) { target.receive(message, sender); } } }
七、替代方案与相关模式
-
观察者模式
- 区别:观察者模式是一对多依赖,中介者模式是集中协调
- 选择:当需要简单的发布-订阅时用观察者,需要复杂协调时用中介者
-
门面模式
- 区别:门面模式简化接口,中介者模式协调交互
- 选择:隐藏子系统复杂性用门面,协调子系统交互用中介者
-
代理模式
- 区别:代理控制访问,中介者协调通信
- 选择:控制对单个对象的访问用代理,协调多个对象的交互用中介者
-
事件总线
- 类似:都是集中式事件处理
- 区别:事件总线更通用,支持任意组件间通信
- 选择:需要松耦合的全局事件系统用事件总线
-
服务网格
- 类似:都是协调分布式组件
- 区别:服务网格是基础设施层,中介者是应用层
- 选择:微服务架构用服务网格,单体应用内部协调用中介者
八、实际应用建议
8.1 何时使用中介者模式
- 系统组件间交互复杂:多个对象间有复杂的引用关系
- 需要集中控制:希望将交互逻辑集中管理
- 对象可重用性重要:希望对象能够独立于其他对象被重用
- 交互协议频繁变化:对象间的通信方式经常变化
8.2 何时避免中介者模式
- 简单交互:对象间只有简单的、直接的交互
- 性能敏感:中介者可能成为性能瓶颈
- 已存在合适框架:如消息队列、事件总线等
- 系统规模小:引入中介者会增加不必要的复杂度
8.3 设计建议
- 从简单开始:先用直接引用,必要时重构为中介者
- 保持中介者轻量:避免成为"上帝对象"
- 考虑异步:对于耗时操作,使用异步通信
- 添加日志:中介者是理想的日志记录点
- 支持测试:通过接口抽象,便于单元测试
总结
中介者模式是降低系统复杂度的有效工具,特别适用于对象间存在复杂网状交互的场景。它通过引入中介者对象,将多对多交互转变为一对多交互,显著提高了系统的可维护性和可扩展性。
关键价值:在对象间交互复杂、变化频繁的系统中,中介者模式能提供清晰的通信结构和集中的控制点。但需注意避免中介者过度复杂化,保持职责单一,合理划分交互边界。
在实际应用中,应根据系统规模和复杂度权衡是否使用中介者模式。对于小型系统,直接的对象引用可能更简单;对于大型复杂系统,中介者模式能显著改善代码结构和维护性。