中介者设计模式全方位深度解析

3 阅读12分钟

一、模式定义与核心思想

中介者模式是一种行为设计模式,通过引入一个中介者对象来封装一系列对象之间的交互,使这些对象不需要显式地相互引用,从而降低耦合度,并可以独立地改变它们之间的交互。

核心思想:用中介者对象替代对象间的直接交互

  1. 从"多对多"交互变为"一对多"交互
  2. 中介者协调对象间的通信
  3. 对象间不直接知晓彼此,只与中介者通信

二、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 典型应用场景

  1. GUI组件交互

    • 复杂表单验证:多个输入框的联动验证
    • 组件状态同步:复选框、单选框、按钮的启用/禁用状态
    • 对话框组件协调
  2. 消息系统/聊天应用

    • 群聊系统:用户不直接通信,通过聊天室转发
    • 消息路由:根据消息类型路由到不同处理程序
    • 发布-订阅系统的中心调度
  3. 空中交通管制/调度系统

    • 飞机间的冲突检测与解决
    • 资源分配与调度
    • 状态同步与通知
  4. 工作流引擎

    • 任务协调与分配
    • 状态流转控制
    • 参与者间的通信协调
  5. 微服务架构中的API网关

    • 请求路由与聚合
    • 服务发现与负载均衡
    • 认证授权的统一处理
  6. 游戏开发

    • 游戏实体间的交互协调
    • 碰撞检测与处理
    • 状态同步与广播
  7. 企业应用集成

    • 系统间数据交换
    • 业务流程编排
    • 事件驱动架构的事件总线

3.2 使用时机判断

  • ✅ 对象间存在复杂的网状引用关系
  • ✅ 对象间通信逻辑复杂,难以理解和维护
  • ✅ 需要集中控制多个对象间的交互
  • ✅ 对象间的依赖关系导致复用困难
  • ✅ 需要在运行时动态改变对象间的交互行为

四、优缺点分析

4.1 优点

  1. 单一职责原则:将对象间复杂的交互逻辑抽取到中介者中
  2. 开闭原则:新增同事类时无需修改现有同事类
  3. 降低耦合度:对象间不直接依赖,只依赖中介者
  4. 简化对象协议:从多对多交互简化为一对多交互
  5. 集中控制:所有交互逻辑集中在中介者,易于理解和维护
  6. 减少子类生成:将行为分布在各个同事类之间,而不是通过继承

4.2 缺点

  1. 中介者可能过于复杂:随着交互逻辑增加,中介者可能成为"上帝对象"
  2. 性能瓶颈:所有通信都经过中介者,可能成为性能瓶颈
  3. 单点故障:中介者故障会影响整个系统
  4. 增加新交互困难:需要修改中介者,可能违反开闭原则
  5. 调试困难:交互逻辑集中,难以追踪具体交互路径
  6. 可能引入循环依赖:中介者和同事类相互引用

五、模式变体与改进

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 使用注意事项

  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;
                // ...
            }
        }
    }
    
  2. 避免循环调用

    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;
            }
        }
    }
    
  3. 线程安全性

    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 最佳实践

  1. 明确中介者职责

    • 中介者应专注于协调,不处理业务逻辑
    • 业务逻辑应放在同事类中
  2. 保持同事类独立

    • 同事类不应知道其他同事类的存在
    • 只通过接口与中介者通信
  3. 使用接口抽象

    java
    java
    下载
    复制
    public interface IMediator {
        void send(String message, IColleague sender);
    }
    
    public interface IColleague {
        void receive(String message, IColleague sender);
        void setMediator(IMediator mediator);
    }
    
  4. 支持动态中介者切换

    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;
        }
    }
    
  5. 实现中介者注册机制

    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);
            }
        }
    }
    

七、替代方案与相关模式

  1. 观察者模式

    • 区别:观察者模式是一对多依赖,中介者模式是集中协调
    • 选择:当需要简单的发布-订阅时用观察者,需要复杂协调时用中介者
  2. 门面模式

    • 区别:门面模式简化接口,中介者模式协调交互
    • 选择:隐藏子系统复杂性用门面,协调子系统交互用中介者
  3. 代理模式

    • 区别:代理控制访问,中介者协调通信
    • 选择:控制对单个对象的访问用代理,协调多个对象的交互用中介者
  4. 事件总线

    • 类似:都是集中式事件处理
    • 区别:事件总线更通用,支持任意组件间通信
    • 选择:需要松耦合的全局事件系统用事件总线
  5. 服务网格

    • 类似:都是协调分布式组件
    • 区别:服务网格是基础设施层,中介者是应用层
    • 选择:微服务架构用服务网格,单体应用内部协调用中介者

八、实际应用建议

8.1 何时使用中介者模式

  1. 系统组件间交互复杂:多个对象间有复杂的引用关系
  2. 需要集中控制:希望将交互逻辑集中管理
  3. 对象可重用性重要:希望对象能够独立于其他对象被重用
  4. 交互协议频繁变化:对象间的通信方式经常变化

8.2 何时避免中介者模式

  1. 简单交互:对象间只有简单的、直接的交互
  2. 性能敏感:中介者可能成为性能瓶颈
  3. 已存在合适框架:如消息队列、事件总线等
  4. 系统规模小:引入中介者会增加不必要的复杂度

8.3 设计建议

  1. 从简单开始:先用直接引用,必要时重构为中介者
  2. 保持中介者轻量:避免成为"上帝对象"
  3. 考虑异步:对于耗时操作,使用异步通信
  4. 添加日志:中介者是理想的日志记录点
  5. 支持测试:通过接口抽象,便于单元测试

总结

中介者模式是降低系统复杂度的有效工具,特别适用于对象间存在复杂网状交互的场景。它通过引入中介者对象,将多对多交互转变为一对多交互,显著提高了系统的可维护性和可扩展性。

关键价值:在对象间交互复杂、变化频繁的系统中,中介者模式能提供清晰的通信结构和集中的控制点。但需注意避免中介者过度复杂化,保持职责单一,合理划分交互边界。

在实际应用中,应根据系统规模和复杂度权衡是否使用中介者模式。对于小型系统,直接的对象引用可能更简单;对于大型复杂系统,中介者模式能显著改善代码结构和维护性。