Skip to content

中介者模式(Mediator Pattern) ​

一、模式概述 ​

中介者模式是一种行为型设计模式,它定义了一个中介对象来封装一组对象的交互方式。中介者模式使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

核心思想:用一个中介对象来封装一系列对象之间的交互,避免对象之间的直接引用。就像机场的塔台,所有飞机不需要直接互相通信,而是通过塔台来协调。

二、角色说明 ​

角色说明
Mediator(抽象中介者)定义同事对象注册和通信的接口
ConcreteMediator(具体中介者)实现协调逻辑,知道所有同事对象,负责协调它们的交互
Colleague(抽象同事)定义同事对象的接口,持有中介者引用
ConcreteColleague(具体同事)实现同事对象,通过中介者与其他同事通信

三、生活中的例子 ​

  • 机场塔台:飞机不直接通信,而是通过塔台协调起降
  • 聊天室:用户不直接互相发消息,而是通过聊天室服务器转发
  • MVC 架构:Controller 充当 View 和 Model 的中介者
  • 房屋中介:买卖双方不直接交易,通过中介者协调
  • UI 表单:表单中的各个组件通过中介者协调状态

四、示例1:聊天室系统 ​

问题场景 ​

一个聊天室中,用户发送的消息需要通过聊天室服务器转发给其他用户。

java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// ========== 抽象中介者 ==========

/**
 * 聊天室(抽象中介者)
 */
interface ChatRoom {
    void register(ChatUser user);
    void sendMessage(String message, ChatUser sender);
    void sendPrivateMessage(String message, ChatUser sender, String receiverName);
}

// ========== 抽象同事 ==========

/**
 * 聊天用户(抽象同事)
 */
abstract class ChatUser {
    protected String name;
    protected ChatRoom chatRoom;

    public ChatUser(String name) {
        this.name = name;
    }

    /**
     * 加入聊天室
     */
    public void joinRoom(ChatRoom room) {
        this.chatRoom = room;
        room.register(this);
        System.out.println("[" + name + "] 加入聊天室");
    }

    /**
     * 发送消息(公开)
     */
    public void send(String message) {
        System.out.println("\n[" + name + "] 发送消息: " + message);
        chatRoom.sendMessage(message, this);
    }

    /**
     * 发送私聊消息
     */
    public void sendTo(String message, String receiverName) {
        System.out.println("\n[" + name + "] 发送私聊给 " + receiverName + ": " + message);
        chatRoom.sendPrivateMessage(message, this, receiverName);
    }

    /**
     * 接收消息
     */
    public abstract void receive(String message, String senderName);

    public String getName() { return name; }
}

// ========== 具体同事 ==========

/**
 * 普通用户
 */
class NormalUser extends ChatUser {
    public NormalUser(String name) {
        super(name);
    }

    @Override
    public void receive(String message, String senderName) {
        System.out.println("  [" + name + "] 收到来自 " + senderName + " 的消息: " + message);
    }
}

/**
 * 管理员用户
 */
class AdminUser extends ChatUser {
    public AdminUser(String name) {
        super(name);
    }

    @Override
    public void receive(String message, String senderName) {
        System.out.println("  [管理员 " + name + "] 收到来自 " + senderName + " 的消息: " + message);
    }

    /**
     * 管理员可以发送系统公告
     */
    public void sendAnnouncement(String message) {
        System.out.println("\n[管理员 " + name + "] 发送系统公告: " + message);
        chatRoom.sendMessage("【系统公告】" + message, this);
    }
}

// ========== 具体中介者 ==========

/**
 * 聊天室服务器(具体中介者)
 * 负责消息的转发和用户管理
 */
class ChatRoomServer implements ChatRoom {
    private final Map<String, ChatUser> users = new HashMap<>();

    @Override
    public void register(ChatUser user) {
        users.put(user.getName(), user);
        System.out.println("  [聊天室] " + user.getName() + " 已注册");
    }

    @Override
    public void sendMessage(String message, ChatUser sender) {
        // 将消息转发给除发送者以外的所有在线用户
        for (Map.Entry<String, ChatUser> entry : users.entrySet()) {
            if (!entry.getKey().equals(sender.getName())) {
                entry.getValue().receive(message, sender.getName());
            }
        }
    }

    @Override
    public void sendPrivateMessage(String message, ChatUser sender, String receiverName) {
        ChatUser receiver = users.get(receiverName);
        if (receiver != null) {
            receiver.receive("[私聊] " + message, sender.getName());
        } else {
            System.out.println("  [聊天室] 用户 " + receiverName + " 不在线!");
        }
    }
}

// ========== 客户端代码 ==========

public class ChatRoomDemo {
    public static void main(String[] args) {
        // 创建聊天室服务器(中介者)
        ChatRoomServer server = new ChatRoomServer();

        // 创建用户(同事)
        ChatUser alice = new NormalUser("Alice");
        ChatUser bob = new NormalUser("Bob");
        ChatUser charlie = new NormalUser("Charlie");
        AdminUser admin = new AdminUser("Admin");

        // 加入聊天室
        System.out.println("===== 用户加入聊天室 =====");
        alice.joinRoom(server);
        bob.joinRoom(server);
        charlie.joinRoom(server);
        admin.joinRoom(server);

        // 公开聊天
        System.out.println("\n===== 公开聊天 =====");
        alice.send("大家好!");
        bob.send("你好 Alice!");

        // 私聊
        System.out.println("\n===== 私聊 =====");
        alice.sendTo("Hi Bob, 你好!", "Bob");

        // 管理员公告
        System.out.println("\n===== 系统公告 =====");
        admin.sendAnnouncement("服务器将于今晚10点维护");
    }
}

运行结果 ​

===== 用户加入聊天室 =====
  [聊天室] Alice 已注册
[Alice] 加入聊天室
  [聊天室] Bob 已注册
[Bob] 加入聊天室
  [聊天室] Charlie 已注册
[Charlie] 加入聊天室
  [聊天室] Admin 已注册
[Admin] 加入聊天室

===== 公开聊天 =====

[Alice] 发送消息: 大家好!
  [Bob] 收到来自 Alice 的消息: 大家好!
  [Charlie] 收到来自 Alice 的消息: 大家好!
  [管理员 Admin] 收到来自 Alice 的消息: 大家好!

[Bob] 发送消息: 你好 Alice!
  [Alice] 收到来自 Bob 的消息: 你好 Alice!
  [Charlie] 收到来自 Bob 的消息: 你好 Alice!
  [管理员 Admin] 收到来自 Bob 的消息: 你好 Alice!

===== 私聊 =====

[Alice] 发送私聊给 Bob: Hi Bob, 你好!
  [Bob] 收到来自 Alice 的消息: [私聊] Hi Bob, 你好!

===== 系统公告 =====

[管理员 Admin] 发送系统公告: 服务器将于今晚10点维护
  [Alice] 收到来自 Admin 的消息: 【系统公告】服务器将于今晚10点维护
  [Bob] 收到来自 Admin 的消息: 【系统公告】服务器将于今晚10点维护
  [Charlie] 收到来自 Admin 的消息: 【系统公告】服务器将于今晚10点维护

五、示例2:UI 表单验证中介者 ​

问题场景 ​

一个注册表单包含用户名、密码、确认密码、邮箱等字段,字段之间有联动验证关系。

java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// ========== 抽象中介者 ==========

/**
 * 表单中介者接口
 */
interface FormMediator {
    void registerComponent(FormComponent component);
    void valueChanged(FormComponent source);
    void notifyAll();
}

// ========== 抽象同事 ==========

/**
 * 表单组件(抽象同事)
 */
abstract class FormComponent {
    protected String name;
    protected FormMediator mediator;
    protected boolean valid = true;
    protected String errorMessage = "";

    public FormComponent(String name) {
        this.name = name;
    }

    public void setMediator(FormMediator mediator) {
        this.mediator = mediator;
    }

    public String getName() { return name; }
    public boolean isValid() { return valid; }
    public String getErrorMessage() { return errorMessage; }

    public abstract String getValue();
    public abstract void setValue(String value);
    public abstract void validate();

    @Override
    public String toString() {
        return valid ? name + ": 正常" : name + ": 错误(" + errorMessage + ")";
    }
}

// ========== 具体同事 ==========

/**
 * 文本输入框
 */
class TextInput extends FormComponent {
    private String value = "";

    public TextInput(String name) {
        super(name);
    }

    @Override
    public String getValue() { return value; }

    @Override
    public void setValue(String value) {
        this.value = value;
        validate();
        if (mediator != null) {
            mediator.valueChanged(this);
        }
    }

    @Override
    public void validate() {
        valid = true;
        errorMessage = "";
    }
}

/**
 * 用户名输入框
 */
class UsernameInput extends TextInput {
    public UsernameInput() {
        super("用户名");
    }

    @Override
    public void validate() {
        if (value == null || value.isEmpty()) {
            valid = false;
            errorMessage = "用户名不能为空";
        } else if (value.length() < 3) {
            valid = false;
            errorMessage = "用户名至少3个字符";
        } else if (value.length() > 20) {
            valid = false;
            errorMessage = "用户名不能超过20个字符";
        } else {
            valid = true;
            errorMessage = "";
        }
    }
}

/**
 * 密码输入框
 */
class PasswordInput extends TextInput {
    public PasswordInput() {
        super("密码");
    }

    @Override
    public void validate() {
        if (value == null || value.isEmpty()) {
            valid = false;
            errorMessage = "密码不能为空";
        } else if (value.length() < 6) {
            valid = false;
            errorMessage = "密码至少6个字符";
        } else {
            valid = true;
            errorMessage = "";
        }
    }
}

/**
 * 确认密码输入框
 * 需要和密码字段联动验证
 */
class ConfirmPasswordInput extends TextInput {
    private PasswordInput passwordInput;

    public ConfirmPasswordInput(PasswordInput passwordInput) {
        super("确认密码");
        this.passwordInput = passwordInput;
    }

    @Override
    public void validate() {
        if (value == null || value.isEmpty()) {
            valid = false;
            errorMessage = "确认密码不能为空";
        } else if (!value.equals(passwordInput.getValue())) {
            valid = false;
            errorMessage = "两次输入的密码不一致";
        } else {
            valid = true;
            errorMessage = "";
        }
    }
}

/**
 * 邮箱输入框
 */
class EmailInput extends TextInput {
    public EmailInput() {
        super("邮箱");
    }

    @Override
    public void validate() {
        if (value == null || value.isEmpty()) {
            valid = false;
            errorMessage = "邮箱不能为空";
        } else if (!value.contains("@") || !value.contains(".")) {
            valid = false;
            errorMessage = "邮箱格式不正确";
        } else {
            valid = true;
            errorMessage = "";
        }
    }
}

// ========== 具体中介者 ==========

/**
 * 注册表单(具体中介者)
 * 协调各个表单组件之间的验证关系
 */
class RegistrationForm implements FormMediator {
    private final Map<String, FormComponent> components = new HashMap<>();
    private final List<FormComponent> componentList = new ArrayList<>();
    private Runnable onSubmit;
    private Runnable onError;

    @Override
    public void registerComponent(FormComponent component) {
        component.setMediator(this);
        components.put(component.getName(), component);
        componentList.add(component);
    }

    @Override
    public void valueChanged(FormComponent source) {
        // 某个组件值改变时,重新验证所有组件
        System.out.println("  [中介者] " + source.getName() + " 值改变,触发联动验证");
        notifyAll();
    }

    @Override
    public void notifyAll() {
        System.out.println("  [中介者] 开始验证所有组件...");
        boolean allValid = true;

        for (FormComponent component : componentList) {
            component.validate();
            System.out.println("    " + component);
            if (!component.isValid()) {
                allValid = false;
            }
        }

        if (allValid) {
            System.out.println("  [中介者] 验证通过!提交按钮可用");
            if (onSubmit != null) onSubmit.run();
        } else {
            System.out.println("  [中介者] 验证失败!提交按钮禁用");
            if (onError != null) onError.run();
        }
    }

    public void setOnSubmit(Runnable onSubmit) { this.onSubmit = onSubmit; }
    public void setOnError(Runnable onError) { this.onError = onError; }
}

// ========== 客户端代码 ==========

public class FormMediatorDemo {
    public static void main(String[] args) {
        // 创建表单中介者
        RegistrationForm form = new RegistrationForm();

        // 创建表单组件
        UsernameInput username = new UsernameInput();
        PasswordInput password = new PasswordInput();
        ConfirmPasswordInput confirmPassword = new ConfirmPasswordInput(password);
        EmailInput email = new EmailInput();

        // 注册组件
        form.registerComponent(username);
        form.registerComponent(password);
        form.registerComponent(confirmPassword);
        form.registerComponent(email);

        // 设置提交/错误回调
        form.setOnSubmit(() -> System.out.println("  >>> 表单提交成功!"));
        form.setOnError(() -> System.out.println("  >>> 表单验证失败,请修正错误"));

        // 模拟用户输入
        System.out.println("===== 输入用户名 =====");
        username.setValue("ab");  // 太短

        System.out.println("\n===== 输入密码 =====");
        password.setValue("12345");  // 太短

        System.out.println("\n===== 输入确认密码 =====");
        confirmPassword.setValue("12345678");  // 不一致

        System.out.println("\n===== 输入邮箱 =====");
        email.setValue("invalid-email");  // 格式错误

        System.out.println("\n===== 修正所有输入 =====");
        username.setValue("zhangsan");
        password.setValue("password123");
        confirmPassword.setValue("password123");
        email.setValue("zhangsan@example.com");
    }
}

六、UML 类图 ​

┌──────────────┐
│    Client     │──创建──▶ Mediator
└──────────────┘
                  │
                  ▼
┌──────────────────────────┐       ┌──────────────────────┐
│   Mediator (抽象中介者)    │◀──────│   Colleague (抽象同事) │
│   +register()            │ 通知  │   -mediator: Mediator│
│   +notify()              │◀──────│   +receive()         │
└──────────┬───────────────┘       └──────────┬───────────┘
           │ 实现                              │ 继承
┌──────────┴───────────┐            ┌─────────┴─────────┐
│  ConcreteMediator    │            │ConcreteColleague1  │
│  -colleagues: List   │───────────▶│  +action()         │
│  +notifyAll()        │            └───────────────────┘
└──────────────────────┘            ┌───────────────────┐
                                    │ConcreteColleague2  │
                                    │  +action()         │
                                    └───────────────────┘

七、中介者模式在 Spring 中的应用 ​

java
/**
 * Spring MVC 中的 DispatcherServlet 就是典型的中介者模式
 *
 * DispatcherServlet(中介者)协调:
 * - HandlerMapping(处理器映射)
 * - HandlerAdapter(处理器适配器)
 * - ViewResolver(视图解析器)
 * - Controller(控制器)
 *
 * 请求流程:
 * Client -> DispatcherServlet -> HandlerMapping -> Controller
 *                                    ↓
 * Client <- DispatcherServlet <- ViewResolver <- Model
 */

/**
 * Spring Event 也是中介者模式的体现
 *
 * ApplicationEventPublisher 充当中介者
 * ApplicationEvent 是消息
 * ApplicationListener 是同事对象
 *
 * @Component
 * public class OrderEventPublisher {
 *     @Autowired
 *     private ApplicationEventPublisher publisher;
 *
 *     public void createOrder(Order order) {
 *         // ... 业务逻辑
 *         publisher.publishEvent(new OrderCreatedEvent(order));
 *     }
 * }
 *
 * @Component
 * public class OrderEventHandler {
 *     @EventListener
 *     public void handleOrderCreated(OrderCreatedEvent event) {
 *         // 处理订单创建事件
 *     }
 * }
 */

八、优缺点分析 ​

优点 ​

  1. 减少类间耦合:同事对象之间不需要直接引用,通过中介者通信
  2. 集中控制:交互逻辑集中在中介者中,便于修改和维护
  • 符合迪米特法则:同事对象只需要知道中介者,不需要知道其他同事
  • 易于扩展:新增同事对象只需要在中介者中注册即可

缺点 ​

  1. 中介者过于复杂:随着同事对象增多,中介者可能变得非常复杂(上帝类)
  2. 单点故障:中介者一旦出问题,整个系统都会受影响
  3. 性能问题:所有交互都通过中介者,可能成为性能瓶颈

九、使用场景 ​

  • 聊天系统:消息通过服务器转发
  • MVC 架构:Controller 充当 View 和 Model 的中介
  • 飞机场塔台:协调飞机的起降
  • 表单验证:表单组件之间的联动验证
  • 事件总线:通过事件总线实现组件之间的解耦通信
  • 微服务网关:API Gateway 充当服务之间的中介

十、与其他模式的关系 ​

  • 观察者模式 vs 中介者模式:观察者模式是多对多,中介者模式是多对一
  • 代理模式 vs 中介者模式:代理控制对单一对象的访问,中介者协调多个对象的交互
  • 外观模式 vs 中介者模式:外观简化子系统接口,中介者协调同事对象的交互
  • 观察者模式 + 中介者模式:中介者可以用观察者模式来实现通知机制

十一、最佳实践 ​

  1. 保持中介者的简洁性:不要让中介者承担过多的职责
  2. 合理使用事件机制:结合观察者模式,使用事件来解耦
  3. 考虑性能影响:在高频交互场景中,中介者可能成为瓶颈
  4. 避免中介者成为上帝类:可以通过分层中介者来降低复杂度
  5. 使用接口解耦:同事对象依赖抽象中介者接口,而不是具体实现
java
// 推荐:依赖抽象中介者
interface Mediator {
    void notify(Colleague sender, String event);
}

class ConcreteColleague extends Colleague {
    private Mediator mediator;  // 依赖接口

    public ConcreteColleague(Mediator mediator) {
        super(mediator);
    }
}

总结:中介者模式的核心是"集中控制对象间的交互"。它将网状的多对多交互转化为星形的一对多交互,大大降低了对象之间的耦合度。在实际应用中,MVC 架构、聊天系统、事件总线等都是中介者模式的典型应用。使用时要注意保持中介者的职责清晰,避免使其成为过于复杂的上帝类。