← 返回题目列表

中介者模式有哪些角色?调用流程是什么?

高频 简单 第 2 / 25 题 更新于 2026/07/28
中介者模式MediatorColleague调用流程设计模式

简化版

中介者模式主要有中介者接口、具体中介者、同事对象三个角色。调用流程是同事对象把事件交给中介者,中介者根据规则协调其他同事对象。

详细版

中介者模式的角色通常包括:

  1. Mediator:定义同事对象通知中介者的方法;
  2. ConcreteMediator:持有多个同事对象,集中处理协作逻辑;
  3. Colleague:参与协作的对象,只依赖中介者,不直接依赖其他同事对象。

典型流程是:

  1. 某个同事对象发生动作;
  2. 它调用中介者方法,比如 changed(this)notify(event)
  3. 中介者判断事件来源和业务规则;
  4. 中介者调用其他相关同事对象完成联动。

这样做的好处是,同事对象之间不需要互相知道,复杂交互集中在中介者中维护。

完整版教学

一、为什么角色划分很重要

中介者模式看起来像“对象都找一个中间人说话”,但真正写代码时,容易写成两种坏结构:

  • 同事对象仍然互相调用,中介者只是摆设;
  • 中介者吞掉所有业务能力,变成一个巨大的流程类。

所以理解角色边界非常关键。

中介者模式的重点是:同事对象负责自身行为,中介者负责协作关系。

二、Mediator:通信入口

Mediator 是同事对象和协调逻辑之间的接口。

它通常长这样:

interface DialogMediator {
    void componentChanged(Component component);
}

也可以按事件类型设计:

interface Mediator {
    void notify(String event, Object data);
}

接口的作用是降低同事对象对具体中介者的依赖。同事对象只知道“我把变化告诉中介者”,不关心中介者里面如何协调。

三、ConcreteMediator:协作规则中心

具体中介者负责真正的协调逻辑。

例如登录弹窗里有用户名输入框、密码输入框、登录按钮:

  • 用户名为空时禁用登录按钮;
  • 密码为空时禁用登录按钮;
  • 两者都有值时启用登录按钮。

这些规则如果写在输入框里,输入框就要知道按钮;如果写在按钮里,按钮又要知道输入框。更合适的做法是放在中介者里。

class LoginDialogMediator implements DialogMediator {
    private TextBox username;
    private TextBox password;
    private Button loginButton;

    public void setComponents(TextBox username, TextBox password, Button loginButton) {
        this.username = username;
        this.password = password;
        this.loginButton = loginButton;
    }

    @Override
    public void componentChanged(Component component) {
        boolean enable = username.hasText() && password.hasText();
        loginButton.setEnabled(enable);
    }
}

这里中介者知道多个组件,也知道组件之间的联动规则。

四、Colleague:同事对象

同事对象是参与协作的业务对象。

它一般持有中介者引用:

abstract class Component {
    protected DialogMediator mediator;

    protected Component(DialogMediator mediator) {
        this.mediator = mediator;
    }
}

具体组件只在自己变化时通知中介者:

class TextBox extends Component {
    private String text = "";

    public TextBox(DialogMediator mediator) {
        super(mediator);
    }

    public void setText(String text) {
        this.text = text;
        mediator.componentChanged(this);
    }

    public boolean hasText() {
        return text != null && !text.isBlank();
    }
}

注意,TextBox 不需要知道 Button,这就是解耦效果。

五、调用流程怎么串起来

完整流程可以这样描述:

用户输入用户名

TextBox.setText()

TextBox 通知 mediator.componentChanged(this)

Mediator 读取 username/password 状态

Mediator 设置 loginButton 是否可点击

面试时讲清楚这个流程,比只背角色名更有说服力。

六、角色边界怎么把握

一个实用判断是:

  • “我自己的状态怎么变”属于同事对象;
  • “我变了以后谁要跟着变”属于中介者;
  • “多个对象之间的协作流程”属于中介者;
  • “单个对象内部的核心能力”不应该被中介者拿走。

如果中介者开始处理每个组件内部细节,就说明边界失控了。

七、常见误区与追问

角色流程从 Colleague 产生事件开始。C1 不直接调用 C2,而是把事件交给 Mediator;Mediator 读取必要上下文后,通知 C2 或 C3 执行动作。以4个控件为例,每个控件只保存一个中介者引用,协调规则集中在对应对话框中介者。

检查维度判定依据
Mediator声明并执行协调协议
ConcreteColleague处理自身逻辑并报告交互事件
C1 --event--> M --command--> C2/C3

记忆钩子:同事上报,中介判断,同事执行。

  • 误区:Mediator 只是消息转发器。 经典中介者可以根据状态和事件选择不同协作路径。
  • 追问:同事如何获得 Mediator? 构造注入、注册或创建阶段装配均可,优先依赖接口。
  • 误区:Mediator 必须维护所有同事集合。 固定场景可持有明确引用,动态场景才需要注册表。
  • 追问:调用流程可以是异步吗? 可以,但必须定义事件顺序、失败与一致性语义。
  • 追问:如何验证依赖方向? 检查任何 Colleague 都不应引用另一个具体 Colleague。

八、加强记忆

中介者模式的角色可以记成“同事发事件,中介做协调”。同事对象只负责自己的状态和行为,变化时通知中介者;具体中介者保存相关对象并执行协作规则;客户端看到的是对象联动变简单了,而代码内部减少的是对象之间互相持有、互相调用的网状依赖。