rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 中介者模式(Mediator Pattern)

  • 定义
  • 意图
  • 结构
  • 类图(UML)
  • 时序图(UML)
  • 模式分析
    • (1)交互方式的转变
    • (2)中介者的 “控制逻辑”
    • (3)同事的 “被动性”
  • 代码实现(Java 版)
    • 步骤 1:定义抽象中介者(IMediator)
    • 步骤 2:定义抽象同事(User)
    • 步骤 3:实现具体中介者(ChatRoom)
    • 步骤 4:实现具体同事(ConcreteUser)
    • 步骤 5:客户端测试(Client)
    • 输出结果
  • 代码实现2
  • 优点
  • 缺点
  • 适用环境
  • 模式应用
  • 模式扩展
    • (1)与单例模式结合
    • (2)与观察者模式结合
    • (3)与命令模式结合
    • (4)纯中介者 vs 不纯中介者
  • Android 中的应用
    • (1)Activity 作为 Fragment 的中介者
    • (2)EventBus/FlowBus
    • (3)ViewModel + LiveData(间接中介)
  • 总结
  • 资料

中介者模式(Mediator Pattern)

中介者模式是行为型设计模式的一种,其核心思想是通过引入一个 “中介者” 对象,协调多个 “同事” 对象之间的交互,避免同事对象之间直接引用,从而降低系统耦合度。下面将按照你要求的 14 个维度进行全面解析。

定义

中介者模式(Mediator Pattern)定义一个对象,封装一系列对象之间的交互方式,使对象之间无需显式引用,降低耦合度,且可以独立地改变它们之间的交互。

对于中介者模式,你脑海中首先映入的肯定就是中介,我们的生活中到处充满着中介,比如说婚姻介绍所,房产中介,甚至于联合国都是中介。他们的作用都是加强处理人与人之间或者是国与国之间的关系。如果没有这种中介会怎么样呢?就以联合国为例,这世界上有200多个国家,每个国家之间的关系是超级复杂的。

简单来说:用 “第三方”(中介者)代替 “多对多” 的直接交互,将复杂的网状关系转化为 “中介者 - 同事” 的星型关系。

意图

  • 减少同事对象之间的直接耦合,避免 “牵一发而动全身” 的修改成本。
  • 将多个对象的交互逻辑集中到中介者中,便于统一管理和维护。
  • 使同事对象可以独立变化(只需适配中介者接口,无需关心其他同事)。
  • 集中相关对象之间复杂的沟通和控制方式。

结构

中介者模式包含 4 个核心角色,各角色职责明确,相互协作完成交互逻辑:

角色名称核心职责
抽象中介者(IMediator)定义中介者与同事对象交互的接口,通常包含 “注册同事” 和 “转发消息” 的抽象方法。
具体中介者(ConcreteMediator)实现抽象中介者接口,维护所有同事对象的引用,负责协调同事间的具体交互逻辑。
抽象同事(IColleague)定义同事对象的接口,持有抽象中介者的引用(确保同事能通过中介者通信),包含 “发送消息” 和 “接收消息” 的抽象方法。
具体同事(ConcreteColleague)实现抽象同事接口,当需要与其他同事交互时,不直接调用其他同事,而是通过中介者转发消息。

类图(UML)

- Mediator: 中介者,定义一个接口用于与各同事(Colleague)对象通信。

- Colleague: 同事,相关对象

中介者模式的类图需体现 “中介者 - 同事” 的星型依赖关系,具体结构如下:

+-------------------+       +-------------------+
|    IMediator      |<------|    IColleague     |
+-------------------+       +-------------------+
| + register(Colleague)    | + mediator: IMediator
| + relay(Colleague, msg)  | + send(msg: String)
+-------------------+       | + receive(msg: String)
        ^                   +-------------------+
        |                          ^
        |                          |
+-------------------+       +-------------------+       +-------------------+
| ConcreteMediator  |       | ConcreteColleagueA|       | ConcreteColleagueB|
+-------------------+       +-------------------+       +-------------------+
| - colleagues: List<Colleague>| + send(msg) { mediator.relay(this, msg) } | + send(msg) { mediator.relay(this, msg) }
| + register(Colleague) {     | + receive(msg) { ... } | + receive(msg) { ... }
|    colleagues.add(c)        +-------------------+       +-------------------+
|  }
| + relay(Colleague, msg) {
|    // 逻辑:根据发送者/消息内容,转发给目标同事
|    for (c : colleagues) {
|      if (c != sender) c.receive(msg);
|    }
|  }
+-------------------+

核心关系说明:

  • 抽象同事(IColleague)持有抽象中介者(IMediator)的引用,确保所有同事都能通过中介者通信。
  • 具体中介者(ConcreteMediator)维护所有具体同事的列表,通过relay方法实现消息转发。

时序图(UML)

时序图用于展示 “同事 A 通过中介者向同事 B 发送消息” 的核心流程,步骤如下:

Client          ConcreteColleagueA    ConcreteMediator    ConcreteColleagueB
   |                    |                    |                    |
   | 1. 创建中介者       |                    |                    |
   |------------------->|                    |                    |
   |                    |                    |                    |
   | 2. 创建同事A并注册  |                    |                    |
   |------------------->| 3. 注册到中介者      |                    |
   |                    |-------------------->|                    |
   |                    |                    |                    |
   | 4. 创建同事B并注册  |                    |                    |
   |---------------------------------------->| 5. 注册到中介者      |
   |                    |                    |<-------------------|
   |                    |                    |                    |
   | 6. 触发同事A发消息  |                    |                    |
   |------------------->|                    |                    |
   |                    |                    |                    |
   |                    | 7. 调用中介者转发    |                    |
   |                    |-------------------->|                    |
   |                    |                    |                    |
   |                    |                    | 8. 转发消息给同事B   |
   |                    |                    |-------------------->|
   |                    |                    |                    |
   |                    |                    | 9. 同事B接收消息    |
   |                    |                    |<-------------------|
   |                    |                    |                    |

流程解析:

  1. 客户端先创建具体中介者,再创建同事对象并注册到中介者中。
  2. 当同事 A 需要发送消息时,调用自身send方法,内部委托中介者的relay方法。
  3. 中介者根据逻辑(如 “转发给所有非发送者同事”)将消息传递给同事 B。
  4. 同事 B 通过receive方法处理消息,全程不与同事 A 直接交互。

模式分析

中介者模式的核心是 “集中化交互逻辑”,需从以下 3 个角度理解:

(1)交互方式的转变

  • 无中介者时:同事对象之间是 “多对多” 网状关系(如 A 需引用 B、C,B 需引用 A、C),耦合度极高。
  • 有中介者时:同事对象与中介者是 “一对多” 星型关系(所有同事只引用中介者,中介者引用所有同事),耦合度大幅降低。

(2)中介者的 “控制逻辑”

中介者不仅是 “消息转发器”,还可封装复杂的业务规则,例如:

  • 只转发消息给特定同事(而非所有同事)。
  • 对消息进行过滤或加工(如格式转换、权限校验)。
  • 协调多个同事的交互顺序(如 A 完成操作后,中介者通知 B 执行下一步)。

(3)同事的 “被动性”

同事对象只需关注自身业务逻辑,无需关心 “消息发给谁”“谁会发消息来”:

  • 发送消息时:调用mediator.relay(this, msg)即可,无需知道目标同事。
  • 接收消息时:实现receive方法处理消息,无需知道发送者。

代码实现(Java 版)

下面通过 “聊天室” 场景实现中介者模式:聊天室(中介者)协调多个用户(同事)发送消息,用户发送的消息通过聊天室转发给其他所有用户。

步骤 1:定义抽象中介者(IMediator)

// 抽象中介者:定义注册用户和转发消息的接口
public interface IMediator {
    // 注册用户(同事)到中介者
    void register(User user);
    // 转发消息:参数1=发送者,参数2=消息内容
    void relayMessage(User sender, String message);
}

步骤 2:定义抽象同事(User)

// 抽象同事:用户,持有中介者引用
public abstract class User {
    protected IMediator mediator; // 持有中介者引用(核心)
    protected String name;

    public User(IMediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
    }

    // 发送消息(委托给中介者)
    public abstract void sendMessage(String message);

    // 接收消息(处理消息)
    public abstract void receiveMessage(String senderName, String message);
}

步骤 3:实现具体中介者(ChatRoom)

// 具体中介者:聊天室,维护所有用户,负责转发消息
import java.util.ArrayList;
import java.util.List;

public class ChatRoom implements IMediator {
    // 维护所有注册的用户(同事)
    private final List<User> userList = new ArrayList<>();

    @Override
    public void register(User user) {
        if (!userList.contains(user)) {
            userList.add(user);
            System.out.println("[聊天室] 用户 " + user.name + " 加入聊天室");
        }
    }

    @Override
    public void relayMessage(User sender, String message) {
        // 逻辑:将消息转发给所有非发送者的用户
        for (User user : userList) {
            if (user != sender) { // 不发给自己
                user.receiveMessage(sender.name, message);
            }
        }
    }
}

步骤 4:实现具体同事(ConcreteUser)

// 具体同事:普通用户
public class ConcreteUser extends User {
    public ConcreteUser(IMediator mediator, String name) {
        super(mediator, name);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("[用户 " + name + "] 发送消息:" + message);
        // 委托中介者转发消息
        mediator.relayMessage(this, message);
    }

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

步骤 5:客户端测试(Client)

// 客户端:模拟用户在聊天室交互
public class MediatorClient {
    public static void main(String[] args) {
        // 1. 创建中介者(聊天室)
        IMediator chatRoom = new ChatRoom();

        // 2. 创建同事(用户)并注册到中介者
        User alice = new ConcreteUser(chatRoom, "Alice");
        User bob = new ConcreteUser(chatRoom, "Bob");
        User charlie = new ConcreteUser(chatRoom, "Charlie");
        chatRoom.register(alice);
        chatRoom.register(bob);
        chatRoom.register(charlie);

        // 3. 模拟用户发送消息
        System.out.println("\n===== 消息交互开始 =====");
        alice.sendMessage("大家好!我是Alice~");
        System.out.println("------------------------");
        bob.sendMessage("Hi Alice!我是Bob~");
        System.out.println("------------------------");
        charlie.sendMessage("欢迎加入聊天室!");
    }
}

输出结果

[聊天室] 用户 Alice 加入聊天室
[聊天室] 用户 Bob 加入聊天室
[聊天室] 用户 Charlie 加入聊天室

===== 消息交互开始 =====
[用户 Alice] 发送消息:大家好!我是Alice~
[用户 Bob] 收到 Alice 的消息:大家好!我是Alice~
[用户 Charlie] 收到 Alice 的消息:大家好!我是Alice~
------------------------
[用户 Bob] 发送消息:Hi Alice!我是Bob~
[用户 Alice] 收到 Bob 的消息:Hi Alice!我是Bob~
[用户 Charlie] 收到 Bob 的消息:Hi Alice!我是Bob~
------------------------
[用户 Charlie] 发送消息:欢迎加入聊天室!
[用户 Alice] 收到 Charlie 的消息:欢迎加入聊天室!
[用户 Bob] 收到 Charlie 的消息:欢迎加入聊天室!

代码实现2

Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:

使用中介者模式可以将复杂的依赖结构变成星形结构:

public abstract class Colleague {

    public abstract void onEvent(Mediator mediator);

}

public class Alarm extends Colleague {



    @Override

    public void onEvent(Mediator mediator) {

        mediator.doEvent("alarm");

    }



    public void doAlarm() {

        System.out.println("doAlarm()");

    }

}

public class CoffeePot extends Colleague {

    @Override

    public void onEvent(Mediator mediator) {

        mediator.doEvent("coffeePot");

    }



    public void doCoffeePot() {

        System.out.println("doCoffeePot()");

    }

}

public class Calender extends Colleague {

    @Override

    public void onEvent(Mediator mediator) {

        mediator.doEvent("calender");

    }



    public void doCalender() {

        System.out.println("doCalender()");

    }

}

public class Sprinkler extends Colleague {

    @Override

    public void onEvent(Mediator mediator) {

        mediator.doEvent("sprinkler");

    }



    public void doSprinkler() {

        System.out.println("doSprinkler()");

    }

}

public abstract class Mediator {

    public abstract void doEvent(String eventType);

}

public class ConcreteMediator extends Mediator {

    private Alarm alarm;

    private CoffeePot coffeePot;

    private Calender calender;

    private Sprinkler sprinkler;



    public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) {

        this.alarm = alarm;

        this.coffeePot = coffeePot;

        this.calender = calender;

        this.sprinkler = sprinkler;

    }



    @Override

    public void doEvent(String eventType) {

        switch (eventType) {

            case "alarm":

                doAlarmEvent();

                break;

            case "coffeePot":

                doCoffeePotEvent();

                break;

            case "calender":

                doCalenderEvent();

                break;

            default:

                doSprinklerEvent();

        }

    }



    public void doAlarmEvent() {

        alarm.doAlarm();

        coffeePot.doCoffeePot();

        calender.doCalender();

        sprinkler.doSprinkler();

    }



    public void doCoffeePotEvent() {

        // ...

    }



    public void doCalenderEvent() {

        // ...

    }



    public void doSprinklerEvent() {

        // ...

    }

}

public class Client {

    public static void main(String[] args) {

        Alarm alarm = new Alarm();

        CoffeePot coffeePot = new CoffeePot();

        Calender calender = new Calender();

        Sprinkler sprinkler = new Sprinkler();

        Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);

        // 闹钟事件到达,调用中介者就可以操作相关对象

        alarm.onEvent(mediator);

    }

}

doAlarm()

doCoffeePot()

doCalender()

doSprinkler()

优点

  1. 降低耦合度:同事对象之间无需直接引用,修改一个同事不会影响其他同事,符合 “开闭原则”。
  2. 集中化控制:所有交互逻辑集中在中介者中,便于统一维护和扩展(如新增交互规则只需修改中介者)。
  3. 简化同事代码:同事只需关注自身业务,无需处理复杂的多对象交互逻辑。
  4. 提高可扩展性:新增同事时,只需实现抽象同事接口并注册到中介者,无需修改现有代码。

缺点

  1. 中介者可能成为 “上帝类”:当同事数量多、交互逻辑复杂时,中介者的代码会变得臃肿,职责过重,难以维护。
  2. 中介者与同事的依赖固化:同事必须依赖中介者才能交互,若中介者出现问题,整个系统的交互会受影响。
  3. 可能违反 “单一职责原则”:中介者可能同时处理消息转发、业务校验、顺序协调等多个职责,职责边界模糊。

适用环境

当系统满足以下条件时,适合使用中介者模式:

  • 多个对象之间存在复杂的 “多对多” 交互,导致耦合度极高。
  • 希望集中管理对象之间的交互逻辑,避免分散在各个同事中。
  • 需简化对象之间的引用关系,使系统结构更清晰(如替换网状关系为星型关系)。
  • 需对对象交互进行统一监控、日志记录或权限控制(如中介者中添加日志逻辑)。

模式应用

中介者模式在实际开发中应用广泛,常见场景包括:

  1. GUI 组件交互:
    • 例如:对话框(Dialog)作为中介者,协调按钮(Button)、输入框(Input)、下拉框(Select)的交互(如点击 “提交” 按钮,对话框验证输入框内容,再通知下拉框更新)。
  2. MVC 架构:
    • Controller(控制器)本质是中介者:Model(模型)和 View(视图)不直接交互,Model 数据变化时通过 Controller 通知 View 更新,View 用户操作时通过 Controller 修改 Model。
  3. 消息队列 / 事件总线:
    • 例如:RabbitMQ、EventBus(如 GreenRobot EventBus)作为中介者,发布者(Publisher)和订阅者(Subscriber)通过消息队列交互,无需知道对方存在。
  4. 分布式系统协调:
    • 例如:ZooKeeper 作为中介者,协调分布式服务的注册、发现和配置同步,服务之间无需直接通信。
  5. 聊天室 / 即时通讯:
    • 如前文代码示例,服务器作为中介者,转发用户之间的消息,用户无需知道其他用户的网络地址。

模式扩展

中介者模式可与其他设计模式结合,形成更灵活的解决方案:

(1)与单例模式结合

  • 场景:通常系统中只需一个中介者实例(如聊天室服务器、EventBus)。
  • 实现:将具体中介者设计为单例(如饿汉式、懒汉式),避免重复创建中介者对象。

(2)与观察者模式结合

  • 场景:中介者需感知同事状态变化,或同事需感知中介者的通知。
  • 实现:同事作为 “观察者”,中介者作为 “被观察者”;当同事状态变化时,通知中介者(观察者模式反向),中介者再协调其他同事。

(3)与命令模式结合

  • 场景:中介者需处理复杂的交互命令(如 “提交订单”“取消订单”)。
  • 实现:将不同的交互逻辑封装为命令(Command)对象,中介者通过执行命令来协调同事,降低中介者的职责复杂度。

(4)纯中介者 vs 不纯中介者

  • 纯中介者:所有同事交互必须通过中介者,同事之间完全无直接引用(严格解耦)。
  • 不纯中介者:允许同事之间在特定场景下直接交互(如简单消息无需中介者转发),平衡解耦和性能。

Android 中的应用

在 Android 开发中,中介者模式的典型应用包括:

(1)Activity 作为 Fragment 的中介者

  • 问题:Fragment 之间不能直接通信(若直接new FragmentB()引用,会导致耦合和生命周期问题)。
  • 解决方案:Activity 作为中介者,Fragment 通过 Activity 传递数据或触发操作:
    1. 定义接口(如OnFragmentInteractionListener),Activity 实现该接口。
    2. Fragment 持有接口引用(通过onAttach方法获取 Activity 实例)。
    3. Fragment 需通信时,调用接口方法,Activity 在实现中通知其他 Fragment。
// 1. 定义交互接口(中介者接口)
public interface OnFragmentInteractionListener {
    void onFragmentADataSent(String data);
}

// 2. Activity作为具体中介者
public class MainActivity extends AppCompatActivity implements OnFragmentInteractionListener {
    private FragmentB fragmentB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragmentB = (FragmentB) getSupportFragmentManager().findFragmentById(R.id.fragment_b);
    }

    // 实现接口,转发FragmentA的数据给FragmentB
    @Override
    public void onFragmentADataSent(String data) {
        if (fragmentB != null) {
            fragmentB.updateData(data);
        }
    }
}

// 3. FragmentA(具体同事)
public class FragmentA extends Fragment {
    private OnFragmentInteractionListener listener;

    // 绑定Activity时获取中介者引用
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            listener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException("Activity需实现OnFragmentInteractionListener");
        }
    }

    // 发送数据给FragmentB(通过中介者)
    private void sendDataToFragmentB() {
        if (listener != null) {
            listener.onFragmentADataSent("来自FragmentA的数据");
        }
    }
}

(2)EventBus/FlowBus

  • 原理:EventBus 作为中介者,Activity、Fragment、Service 等组件通过 EventBus 发布(post)和订阅(subscribe)事件,无需直接引用。
  • 优势:简化跨组件通信(如 Activity 通知 Service 执行任务,Service 将结果返回给 Activity)。

(3)ViewModel + LiveData(间接中介)

  • 场景:Activity/Fragment(View)与 Repository(数据源)不直接交互。
  • 原理:ViewModel 作为中介者,View 通过 ViewModel 获取数据,Repository 通过 ViewModel 的 LiveData 通知 View 更新,解耦 View 和数据源。

总结

中介者模式的核心价值是 “解耦多对象交互,集中化控制逻辑”,通过引入中介者,将复杂的网状依赖转化为清晰的星型依赖,使系统更易于维护和扩展。

  • 核心记住:中介者模式不是 “消除耦合”,而是 “将分散的耦合集中到中介者中”,用 “中介者与同事的一对多耦合” 替换 “同事间的多对多耦合”。
  • 使用建议:避免过度依赖中介者 —— 若同事数量少、交互简单,直接交互可能更高效;只有当交互复杂到影响系统可维护性时,再引入中介者,并注意拆分中介者的职责(避免上帝类)。

通过合理使用中介者模式,可以显著提升系统的灵活性和可扩展性,尤其在复杂交互场景中(如 GUI、分布式系统、组件化开发)。

资料

https://zhuanlan.zhihu.com/p/79488538

最近更新:: 2025/10/22 15:36
Contributors: 罗凯文, luokaiwen