文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

深入浅出设计模式【十七、中介者模式】

一、中介者模式介绍

在复杂的软件系统中,经常存在大量对象之间相互通信和调用的关系。如果每个对象都直接持有并调用其他多个对象的引用,会形成一个网状耦合结构。这种结构会导致:

  1. 系统难以理解和维护: 对象间的依赖关系错综复杂。
  2. 可复用性差: 对象因为它与其他对象紧密耦合而无法被单独重用。
  3. 难以扩展: 任何变更都可能引起“涟漪效应”,需要修改多个类。

中介者模式通过引入一个中介者对象来解决这个问题。该对象负责处理不同对象之间的交互。原本直接相互通信的对象现在只与中介者通信,中介者负责将消息转发到适当的对象。这样,对象间的网状耦合就被转化为了星形耦合,极大地降低了系统的复杂度。

二、核心概念与意图

  1. 核心概念

    • 中介者 (Mediator): 定义一个接口,用于与各同事对象进行通信。它通常是一个接口或抽象类,声明了同事对象之间交互的方法。
    • 具体中介者 (Concrete Mediator): 实现中介者接口,协调各同事对象的行为。它了解并维护所有的同事对象,并负责在它们之间传递消息、协调逻辑和依赖关系。
    • 同事类 (Colleague): 定义同事类的抽象父类或接口。它通常持有一个对中介者对象的引用,用于通过中介者与其他同事通信。
    • 具体同事类 (Concrete Colleague): 继承或实现同事类。每个具体同事类在需要与其他同事通信时,不是直接与之交互,而是与它的中介者进行通信。
  2. 意图

    • 用一个中介对象来封装一系列的对象交互
    • 使各对象不需要显式地相互引用,从而使其耦合松散
    • 可以独立地改变对象间的交互,将复杂的交互逻辑集中到中介者中管理。

三、适用场景剖析

中介者模式在以下场景中非常有效:

  1. 系统中对象之间存在复杂的引用关系: 对象间的依赖关系导致系统结构混乱且难以理解。
  2. 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类时: 中介者可以充当这个中间类,将原本分散在多个对象中的交互逻辑集中起来。
  3. 想定制一个分布在多个类中的行为,但又不想在不同类中编写大量代码时: 例如,定义一个复杂的工作流或通信协议,中介者可以成为控制和协调的中心。
  4. 构建组件化或模块化的系统时: 中介者可以作为模块间的通信枢纽,减少模块间的直接依赖。

不适用场景

  • 如果一组对象已经良好定义、通信简单且稳定,引入中介者只会增加不必要的复杂性。
  • 当中介者自身变得过于庞大和复杂时,它可能演变为一个难以维护的“上帝对象”(God Object)。

四、UML 类图解析(Mermaid)

以下UML类图清晰地展示了中介者模式的结构和角色间的关系:

mediator
Client
«interface»
Mediator
+notify(sender: Colleague, event: String)
ConcreteMediator
-colleagueA: ColleagueA
-colleagueB: ColleagueB
-colleagueC: ColleagueC
+notify(sender: Colleague, event: String)
Colleague
-mediator: Mediator
+setMediator(mediator: Mediator)
+onEvent(event: String)
ColleagueA
+operationA()
ColleagueB
+operationB()
ColleagueC
+operationC()
  • Mediator (中介者接口): 声明一个 notify(sender, event) 方法。同事对象通过调用此方法来与中介者通信,告知其发生的事件。
  • ConcreteMediator (具体中介者)
    • 实现 Mediator 接口。
    • 了解并维护所有需要协调的同事对象的引用 (-colleagueA, -colleagueB, -colleagueC)。
    • notify(sender, event) 方法中,包含核心的业务逻辑。它根据发送事件的同事对象 (sender)事件类型 (event),来决定需要通知或调用哪个(或哪些)其他同事对象,以及如何调用。这里是所有交互逻辑的集中地。
  • Colleague (同事基类)
    • 持有对中介者对象的引用 (-mediator: Mediator)。所有同事对象都通过这个引用来与中介者通信。
    • 通常提供一个 onEvent(event) 方法,供中介者在需要时调用(即接收中介者的指令)。
  • ColleagueA, ColleagueB, ColleagueC (具体同事类)
    • 继承自 Colleague
    • 实现自己具体的业务方法(如 operationA())。
    • 当它们的行为需要影响到其他同事时,不会直接调用其他同事,而是调用 mediator.notify(this, "SomeEvent"),将“球”踢给中介者。
  • Client (客户端)
    • 负责创建中介者对象和所有同事对象。
    • 负责将中介者设置到每一个同事对象中(通过 colleague.setMediator(mediator))。
    • 负责将同事对象注册到中介者中(通常在 ConcreteMediator 的构造函数或通过setter方法完成)。

五、各种实现方式及其优缺点

中介者模式的实现关键在于如何设计中介者与同事之间的通信协议。

1. 标准实现(接口 + 事件通知)

即上述UML所描述的方式,同事通过 notify 方法向中介者发送事件。

// 1. Mediator Interface
public interface Mediator {
    void notify(Colleague sender, String event);
}

// 2. Concrete Mediator
public class ConcreteMediator implements Mediator {
    private ColleagueA colleagueA;
    private ColleagueB colleagueB;
    private ColleagueC colleagueC;

    // Setters for colleagues...

    @Override
    public void notify(Colleague sender, String event) {
        // The core logic: how to react to events from colleagues
        if (sender == colleagueA && "eventX".equals(event)) {
            System.out.println("Mediator reacts to A's EventX and triggers B and C.");
            colleagueB.doSomething();
            colleagueC.doSomethingElse();
        } else if (sender == colleagueB && "eventY".equals(event)) {
            System.out.println("Mediator reacts to B's EventY and triggers A.");
            colleagueA.doAnotherThing();
        }
        // ... handle other events from other colleagues
    }
}

// 3. Colleague Base Class
public abstract class Colleague {
    protected Mediator mediator;

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

    public abstract void onEvent(String event); // To be called by Mediator
}

// 4. Concrete Colleague
public class ColleagueA extends Colleague {
    public void operationA() {
        System.out.println("ColleagueA does OperationA.");
        // When something happens that requires coordination, notify the mediator
        mediator.notify(this, "eventX");
    }

    @Override
    public void onEvent(String event) {
        System.out.println("ColleagueA handling event from mediator: " + event);
        // Do something in response to mediator's instruction
    }
}

// 5. Client
public class Client {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();

        ColleagueA a = new ColleagueA();
        ColleagueB b = new ColleagueB();
        ColleagueC c = new ColleagueC();

        a.setMediator(mediator);
        b.setMediator(mediator);
        c.setMediator(mediator);

        mediator.setColleagueA(a);
        mediator.setColleagueB(b);
        mediator.setColleagueC(c);

        a.operationA(); // This will trigger the whole chain via the mediator
    }
}
  • 优点
    • 极大降低了耦合度: 同事类之间完全解耦,它们只依赖于中介者。
    • 集中控制交互: 将对象间复杂的交互逻辑集中到中介者中,使得这些交互行为变得清晰且易于维护。
    • 简化了同事对象: 同事对象可以变得更容易被复用,因为它们不再包含复杂的交互逻辑。
  • 缺点
    • 中介者可能演变为上帝对象: 由于所有交互逻辑都集中在中介者中,它可能变得非常庞大和复杂,难以维护。这是中介者模式最大的风险。

2. 观察者模式与中介者模式的结合

中介者模式天然可以与观察者模式结合。中介者可以被实现为一个事件发布/订阅中心。同事对象不再是调用中介者的 notify 方法,而是发布事件。其他同事对象可以订阅它们关心的事件。中介者负责管理事件订阅和分发。

  • 优点
    • 进一步解耦: 同事对象之间完全不知道彼此的存在,甚至不知道中介者的具体逻辑。它们只是发布和订阅事件。
    • 更灵活: 可以动态地添加或移除事件的订阅者。
  • 缺点
    • 系统行为变得更加间接,跟踪事件流可能会更困难。

六、最佳实践

  1. 避免“上帝对象”: 这是实施中介者模式时最重要的注意事项。不要将所有业务逻辑都塞进中介者。中介者应该只包含对象间的交互逻辑,而不应包含核心业务逻辑。如果中介者变得过于复杂,考虑:
    • 拆分中介者: 根据功能模块创建多个中介者。
    • 结合其他模式: 使用状态模式来管理中介者的行为,或使用策略模式来使交互算法可替换。
  2. 明确中介者的职责: 中介者的核心职责是协调,而不是实现。它应该指挥同事对象去做事,而不是自己亲力亲为。
  3. 与外观模式 (Facade) 区分
    • 中介者模式: 对象是双向的。同事对象与中介者相互通信、相互影响。目的是协调多个平等对象间的交互
    • 外观模式: 对象是单向的。外观为子系统提供一个简化的接口,客户端只与外观交互。目的是简化接口,隐藏子系统复杂性
  4. 优先使用接口MediatorColleague 都应优先定义为接口,以提高灵活性和可测试性。

七、在开发中的演变和应用

中介者模式的思想是现代分布式系统和前端框架架构的核心:

  1. 消息中间件 (Message Queue / Event Bus): 在微服务架构中,消息队列(如RabbitMQ, Kafka)或事件总线 就是系统级别的中介者。各个微服务(同事)不直接调用其他服务,而是通过向消息队列发布消息或事件来进行异步通信。消息队列负责将消息路由到正确的消费者服务。这完美体现了中介者模式的核心思想。
  2. 前端框架 (Vue, React, Angular): 在前端MVC/MVVM框架中,组件之间的通信是一个经典问题。父组件与子组件、兄弟组件间的通信如果直接进行,会形成网状耦合。这些框架提供的全局事件总线 (Event Bus)状态管理库(如Vuex, Redux) 就扮演了中介者的角色。组件不再直接相互调用,而是通过中介者(事件总线或Store)来分发动作和状态变更。
  3. 航空交通管制系统 (Air Traffic Control): 这是一个经典的现实类比。每架飞机(同事)不需要也知道其他所有飞机的飞行计划。它们只需要与塔台(中介者)通信,由塔台来协调所有飞机的起飞、降落和航线,避免碰撞。

八、真实开发案例(Java语言内部、知名开源框架、工具)

  1. Java Timer Class (java.util.Timer)

    • 从某种角度看,Timer 是一个中介者。你创建多个 TimerTask(同事)并将它们调度到同一个 Timer 上。
    • Timer 对象负责协调这些任务的执行(如单线程顺序执行、处理异常、取消任务),TimerTask 之间并不直接交互。Timer 知道所有被调度的 TimerTask 并管理它们的执行逻辑。
  2. Java MVC - javax.faces.context.FacesContext

    • 在JavaServer Faces (JSF) 框架中,FacesContext 对象包含了处理一个特定请求的所有信息。它是一个访问点,不同的JSF组件(如验证器、转换器、渲染器)可以通过它来交互,而不需要直接引用彼此,在一定程度上扮演了中介者的角色。
  3. Spring Framework - ApplicationEventMulticaster

    • Spring的事件发布/订阅机制是中介者模式与观察者模式结合的典范。
    • 同事: 任何被Spring管理的Bean,既可以发布事件 (ApplicationEventPublisher),也可以监听事件 (@EventListener)。
    • 中介者ApplicationEventMulticaster 接口及其实现(如 SimpleApplicationEventMulticaster)是真正的中介者。它维护了事件类型与监听器的映射关系。当有事件发布时,多播器负责找到所有监听该事件的Bean,并调用它们的监听方法。
    • 这使事件发布者和监听者完全解耦。
  4. Swing GUI - Dialog Boxes

    • 在一个包含多个按钮、输入框、选择框的复杂对话框中,这些UI组件之间的交互(例如:勾选某个复选框会禁用另一个输入框)如果直接写死在组件的事件处理器中,会非常混乱。更好的做法是让这些组件的事件处理器都通知一个中介者(对话框本身或一个专门的控制器),由这个中介者来协调所有组件的状态变化。

九、总结

方面总结
模式类型行为型设计模式
核心意图用一个中介对象来封装一系列对象的交互,使其耦合松散,并可独立地改变交互。
关键角色中介者(Mediator), 具体中介者(ConcreteMediator), 同事(Colleague), 具体同事(ConcreteColleague)
核心机制1. 星形结构: 所有同事只与中介者通信。
2. 事件通知: 同事通过 notify 告知中介者状态变化。
3. 集中协调: 中介者包含所有交互逻辑,指挥同事行动。
主要优点1. 极大降低对象耦合度,变网状结构为星形结构。
2. 集中控制交互,使交互逻辑清晰易维护。
3. 简化个体对象,使其更易于复用。
主要缺点1. 中介者可能演变为上帝对象,变得过于复杂,难以维护。这是最大风险。
适用场景系统中对象间存在复杂的、混乱的引用关系,且交互行为急需集中管理。
最佳实践严防上帝对象;中介者职责应限于协调;可与观察者模式结合;与外观模式区分。
现代应用消息队列/事件总线 (微服务通信),前端状态管理 (Vuex, Redux),全局事件机制 (Spring Events)。
真实案例Spring事件多播器 (ApplicationEventMulticaster),消息中间件 (Kafka, RabbitMQ),GUI协调

中介者模式是降低复杂系统耦合度的利器。它将混乱的交互行为秩序化、集中化,是架构师重构和梳理复杂依赖关系的强大工具。然而,其力量也伴随着风险,即可能创造出难以维护的上帝对象。成功应用此模式的关键在于保持中介者自身的简洁和专注,让它做好“协调员”的本职工作,而非“万事通”。在微服务和前端架构中,其思想以消息中间件和状态管理库的形式大放异彩,成为构建大型、松散耦合系统的基石。

posted @ 2025-08-30 00:19  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源