【从零入门23种设计模式17】行为型之中介者模式

一、中介者模式核心定义

中介者模式是行为型设计模式的一种,核心目的是:

定义一个中介对象来封装一系列对象之间的交互,使原有对象无需直接相互引用,从而降低它们之间的耦合度;同时,使对象间的交互可以独立变化。

简单来说:用一个 "中间者" 代替多个对象之间的直接通信,所有对象只和中介者交互,中介者负责转发 / 协调对象间的请求

核心解决的问题

  1. 解耦组件依赖 :避免组件之间形成 "网状依赖"(A↔B、A↔C、B↔C),转为 "星型依赖"(A→中介者、B→中介者、C→中介者);
  2. 集中管理交互逻辑 :组件间的复杂交互规则统一在中介者中实现,而非分散在各个组件内部;
  3. 提升扩展性 :新增组件时,只需修改中介者的交互规则,无需修改原有组件;
  4. 简化维护 :交互逻辑集中管理,定位问题、修改规则更高效。

生活类比

  • 场景 1 :机场塔台(最经典类比)
    • 组件:各架飞机(起飞、降落、滑行);
    • 中介者:机场塔台;
    • 核心:飞机之间不直接通信(如 "我要降落,你先避让"),而是统一向塔台汇报状态,塔台协调所有飞机的行动,避免碰撞。
  • 场景 2 :微信群聊
    • 组件:群内每个成员;
    • 中介者:微信群;
    • 核心:成员不直接私聊(除非必要),而是在群里发消息,微信群(中介者)将消息转发给所有成员,实现多对多通信。
  • 场景 3 :微服务网关
    • 组件:用户服务、订单服务、支付服务;
    • 中介者:API 网关;
    • 核心:服务之间不直接调用,而是通过网关转发请求、协调调用顺序、处理熔断降级。

标准角色

角色 职责 类比(机场场景) 代码定位
抽象中介者(Mediator) 定义组件与中介者交互的接口(如注册组件、转发消息) 塔台的工作规范(接收汇报、下达指令) 接口 / 抽象类
具体中介者(ConcreteMediator) 实现抽象中介者,持有所有组件引用,封装组件间的交互逻辑 具体的机场塔台(协调飞机起降) 核心实现类(如ChatRoomServiceGateway
抽象同事类(Colleague) 定义组件的统一接口,持有中介者引用,可向中介者发送消息 / 接收指令 飞机的通用规范(汇报状态、接收指令) 接口 / 抽象类
具体同事类(ConcreteColleague) 实现抽象同事类,与中介者交互,不直接与其他同事类通信 具体的飞机(客机、货机) 业务组件类(如UserServiceOrderService

核心 UML 类图

二、群聊中介者

以 "微信群聊" 为例,实现中介者模式的核心逻辑 ------ 这是理解中介者模式最直观的案例,所有用户(同事类)通过群聊(中介者)交互,不直接私聊。

  1. 步骤 1:定义抽象中介者(群聊规范)

    /**

    • 抽象中介者:群聊接口(定义注册用户、发送消息的规范)
      /
      public interface ChatMediator {
      /
      *

      • 注册用户到群聊
        */
        void registerUser(User user);

      /**

      • 转发消息(发送者→群聊→所有其他用户)
        */
        void sendMessage(String message, User sender);
        }
  2. 步骤 2:定义抽象同事类(用户规范)

    /**

    • 抽象同事类:用户接口(持有群聊引用,可发送/接收消息)
      */
      public abstract class User {
      // 持有中介者(群聊)引用(核心:用户只和群聊交互)
      protected ChatMediator mediator;
      protected String username;

      public User(ChatMediator mediator, String username) {
      this.mediator = mediator;
      this.username = username;
      // 注册到群聊
      mediator.registerUser(this);
      }

      /**

      • 发送消息到群聊
        */
        public abstract void send(String message);

      /**

      • 接收来自群聊的消息
        */
        public abstract void receive(String message);

      // Getter
      public String getUsername() {
      return username;
      }
      }

  3. 步骤 3:实现具体中介者(微信群)

    import java.util.ArrayList;
    import java.util.List;

    /**

    • 具体中介者:微信群(封装用户间的消息转发逻辑)
      */
      public class WeChatGroup implements ChatMediator {
      // 持有所有用户引用(同事类)
      private final List<User> users = new ArrayList<>();

      /**

      • 注册用户(添加到群聊)
        */
        @Override
        public void registerUser(User user) {
        if (!users.contains(user)) {
        users.add(user);
        System.out.println("【中介者-微信群】用户 " + user.getUsername() + " 加入群聊");
        }
        }

      /**

      • 转发消息(核心:中介者处理交互逻辑)
      • 规则:发送者的消息转发给群内所有其他用户
        */
        @Override
        public void sendMessage(String message, User sender) {
        System.out.println("\n【中介者-微信群】转发 " + sender.getUsername() + " 的消息:" + message);
        // 遍历所有用户,排除发送者,转发消息
        for (User user : users) {
        if (user != sender) {
        user.receive(message);
        }
        }
        }
        }
  4. 步骤 4:实现具体同事类(普通用户 / 管理员)

    /**

    • 具体同事类1:普通用户
      */
      public class NormalUser extends User {
      public NormalUser(ChatMediator mediator, String username) {
      super(mediator, username);
      }

      /**

      • 发送消息(只调用中介者的方法,不直接找其他用户)
        */
        @Override
        public void send(String message) {
        System.out.println("【同事类-普通用户】" + username + " 发送消息:" + message);
        mediator.sendMessage(message, this);
        }

      /**

      • 接收中介者转发的消息
        */
        @Override
        public void receive(String message) {
        System.out.println("【同事类-普通用户】" + username + " 接收消息:" + message);
        }
        }

    /**

    • 具体同事类2:管理员用户(有特殊权限,如禁言)
      */
      public class AdminUser extends User {
      public AdminUser(ChatMediator mediator, String username) {
      super(mediator, username);
      }

      @Override
      public void send(String message) {
      System.out.println("【同事类-管理员】" + username + " 发送消息:" + message);
      mediator.sendMessage(message, this);
      }

      @Override
      public void receive(String message) {
      System.out.println("【同事类-管理员】" + username + " 接收消息:" + message);
      }

      /**

      • 管理员特殊操作(通过中介者执行,不直接操作用户)
        */
        public void muteUser(User user) {
        System.out.println("\n【同事类-管理员】" + username + " 禁言 " + user.getUsername());
        // 中介者可扩展禁言逻辑,此处简化
        System.out.println("【中介者-微信群】执行禁言操作:" + user.getUsername() + " 被禁言");
        }
        }
  5. 客户端(使用群聊中介者)

    /**

    • 客户端:测试群聊中介者模式
      */
      public class MediatorClient {
      public static void main(String[] args) {
      // 1. 创建中介者(微信群)
      ChatMediator weChatGroup = new WeChatGroup();

      复制代码
       // 2. 创建同事类(用户),自动注册到群聊
       User user1 = new NormalUser(weChatGroup, "张三");
       User user2 = new NormalUser(weChatGroup, "李四");
       User admin = new AdminUser(weChatGroup, "管理员");
      
       // 3. 用户发送消息(只和中介者交互,不直接找其他用户)
       user1.send("大家好,我是张三!");
       user2.send("张三你好,我是李四~");
       admin.send("欢迎新成员加入!");
      
       // 4. 管理员执行特殊操作(通过中介者)
       ((AdminUser) admin).muteUser(user1);

      }
      }

输出结果

复制代码
【中介者-微信群】用户 张三 加入群聊
【中介者-微信群】用户 李四 加入群聊
【中介者-微信群】用户 管理员 加入群聊

【同事类-普通用户】张三 发送消息:大家好,我是张三!
【中介者-微信群】转发 张三 的消息:大家好,我是张三!
【同事类-普通用户】李四 接收消息:大家好,我是张三!
【同事类-管理员】管理员 接收消息:大家好,我是张三!

【同事类-普通用户】李四 发送消息:张三你好,我是李四~
【中介者-微信群】转发 李四 的消息:张三你好,我是李四~
【同事类-普通用户】张三 接收消息:张三你好,我是李四~
【同事类-管理员】管理员 接收消息:张三你好,我是李四~

【同事类-管理员】管理员 发送消息:欢迎新成员加入!
【中介者-微信群】转发 管理员 的消息:欢迎新成员加入!
【同事类-普通用户】张三 接收消息:欢迎新成员加入!
【同事类-普通用户】李四 接收消息:欢迎新成员加入!

【同事类-管理员】管理员 禁言 张三
【中介者-微信群】执行禁言操作:张三 被禁言

三、Spring 实战版(微服务网关中介者)

在业务开发中,中介者模式最核心的实战场景是微服务网关 / 服务编排------ 网关作为中介者,协调用户服务、订单服务、支付服务的交互,避免服务间直接耦合。

  1. 依赖准备(Spring Boot)

    <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
  2. 核心模型定义

    import lombok.Data;

    /**

    • 服务请求上下文(传递请求参数、响应结果)
      */
      @Data
      public class ServiceContext {
      private String userId; // 用户ID
      private String orderId; // 订单ID
      private double amount; // 金额
      private String result; // 处理结果
      private String errorMsg; // 错误信息
      }

    /**

    • 服务类型枚举
      */
      public enum ServiceType {
      USER_SERVICE, // 用户服务
      ORDER_SERVICE, // 订单服务
      PAY_SERVICE // 支付服务
      }
  3. 抽象中介者(服务网关)

    /**

    • 抽象中介者:服务网关接口(协调各微服务交互)
      /
      public interface ServiceGatewayMediator {
      /
      *

      • 注册微服务到网关
        */
        void registerService(MicroService service);

      /**

      • 转发请求到指定服务
        */
        ServiceContext forwardRequest(ServiceType serviceType, ServiceContext context);

      /**

      • 编排服务调用(如:创建订单→扣减库存→支付)
        */
        ServiceContext orchestrateOrderFlow(ServiceContext context);
        }
  4. 抽象同事类(微服务)

    /**

    • 抽象同事类:微服务接口(持有网关引用,处理请求)
      */
      public abstract class MicroService {
      // 持有中介者(网关)引用
      protected ServiceGatewayMediator mediator;
      protected ServiceType serviceType;

      public MicroService(ServiceGatewayMediator mediator, ServiceType serviceType) {
      this.mediator = mediator;
      this.serviceType = serviceType;
      // 注册到网关
      mediator.registerService(this);
      }

      /**

      • 处理网关转发的请求
        */
        public abstract ServiceContext handleRequest(ServiceContext context);

      // Getter
      public ServiceType getServiceType() {
      return serviceType;
      }
      }

  5. 具体中介者(API 网关实现)

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;

    import java.util.HashMap;
    import java.util.Map;

    /**

    • 具体中介者:API网关(核心:协调微服务交互)
      */
      @Slf4j
      @Component
      public class ApiGateway implements ServiceGatewayMediator {
      // 存储所有注册的微服务(服务类型→服务实例)
      private final Map<ServiceType, MicroService> serviceMap = new HashMap<>();

      /**

      • 注册微服务到网关
        */
        @Override
        public void registerService(MicroService service) {
        serviceMap.put(service.getServiceType(), service);
        log.info("【中介者-API网关】注册服务:{}", service.getServiceType());
        }

      /**

      • 转发请求到指定服务(核心:中介者路由请求)
        */
        @Override
        public ServiceContext forwardRequest(ServiceType serviceType, ServiceContext context) {
        log.info("【中介者-API网关】转发请求到 {},用户ID:{},订单ID:{}",
        serviceType, context.getUserId(), context.getOrderId());
        // 获取目标服务
        MicroService service = serviceMap.get(serviceType);
        if (service == null) {
        context.setErrorMsg("服务不存在:" + serviceType);
        return context;
        }
        // 转发请求到服务
        return service.handleRequest(context);
        }

      /**

      • 编排订单流程(复杂交互逻辑集中在中介者)

      • 流程:校验用户→创建订单→支付订单
        */
        @Override
        public ServiceContext orchestrateOrderFlow(ServiceContext context) {
        log.info("【中介者-API网关】开始编排订单流程,订单ID:{}", context.getOrderId());

        // 1. 转发到用户服务,校验用户
        ServiceContext userContext = forwardRequest(ServiceType.USER_SERVICE, context);
        if (userContext.getErrorMsg() != null) {
        log.error("【中介者-API网关】用户校验失败:{}", userContext.getErrorMsg());
        return userContext;
        }

        // 2. 转发到订单服务,创建订单
        ServiceContext orderContext = forwardRequest(ServiceType.ORDER_SERVICE, userContext);
        if (orderContext.getErrorMsg() != null) {
        log.error("【中介者-API网关】创建订单失败:{}", orderContext.getErrorMsg());
        return orderContext;
        }

        // 3. 转发到支付服务,支付订单
        ServiceContext payContext = forwardRequest(ServiceType.PAY_SERVICE, orderContext);
        if (payContext.getErrorMsg() != null) {
        log.error("【中介者-API网关】支付订单失败:{}", payContext.getErrorMsg());
        return payContext;
        }

        // 4. 流程完成
        payContext.setResult("订单流程完成:用户校验通过→订单创建成功→支付成功,订单ID:" + context.getOrderId());
        log.info("【中介者-API网关】订单流程完成,订单ID:{}", context.getOrderId());
        return payContext;
        }
        }

  6. 具体同事类(各微服务实现)

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;

    /**

    • 具体同事类1:用户服务(校验用户合法性)
      */
      @Slf4j
      @Component
      public class UserService extends MicroService {
      public UserService(ServiceGatewayMediator mediator) {
      super(mediator, ServiceType.USER_SERVICE);
      }

      /**

      • 处理用户校验请求(只和网关交互,不直接调用其他服务)
        */
        @Override
        public ServiceContext handleRequest(ServiceContext context) {
        log.info("【同事类-用户服务】校验用户:{}", context.getUserId());
        // 模拟校验逻辑:用户ID以U开头则合法
        if (context.getUserId() == null || !context.getUserId().startsWith("U")) {
        context.setErrorMsg("用户ID非法:" + context.getUserId());
        return context;
        }
        context.setResult("用户校验通过:" + context.getUserId());
        return context;
        }
        }

    /**

    • 具体同事类2:订单服务(创建订单)
      */
      @Slf4j
      @Component
      public class OrderService extends MicroService {
      public OrderService(ServiceGatewayMediator mediator) {
      super(mediator, ServiceType.ORDER_SERVICE);
      }

      @Override
      public ServiceContext handleRequest(ServiceContext context) {
      log.info("【同事类-订单服务】创建订单:{},金额:{}", context.getOrderId(), context.getAmount());
      // 模拟创建逻辑:订单ID以O开头且金额>0则成功
      if (context.getOrderId() == null || !context.getOrderId().startsWith("O")) {
      context.setErrorMsg("订单ID非法:" + context.getOrderId());
      return context;
      }
      if (context.getAmount() <= 0) {
      context.setErrorMsg("订单金额非法:" + context.getAmount());
      return context;
      }
      context.setResult("订单创建成功:" + context.getOrderId());
      return context;
      }
      }

    /**

    • 具体同事类3:支付服务(支付订单)
      */
      @Slf4j
      @Component
      public class PayService extends MicroService {
      public PayService(ServiceGatewayMediator mediator) {
      super(mediator, ServiceType.PAY_SERVICE);
      }

      @Override
      public ServiceContext handleRequest(ServiceContext context) {
      log.info("【同事类-支付服务】支付订单:{},金额:{}", context.getOrderId(), context.getAmount());
      // 模拟支付逻辑:金额>0则支付成功
      if (context.getAmount() <= 0) {
      context.setErrorMsg("支付金额非法:" + context.getAmount());
      return context;
      }
      context.setResult("订单支付成功:" + context.getOrderId() + ",金额:" + context.getAmount());
      return context;
      }
      }

  7. 客户端(Spring Boot 测试)

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;

    /**

    • 客户端:测试微服务网关中介者
      */
      @SpringBootApplication
      public class SpringMediatorDemoApplication {
      public static void main(String[] args) {
      // 1. 启动Spring容器(自动注册网关和微服务)
      ConfigurableApplicationContext context = SpringApplication.run(SpringMediatorDemoApplication.class, args);
      ServiceGatewayMediator gateway = context.getBean(ApiGateway.class);

      复制代码
       // 2. 测试正常订单流程
       System.out.println("======= 测试正常订单流程 =======");
       ServiceContext normalContext = new ServiceContext();
       normalContext.setUserId("U1001");
       normalContext.setOrderId("O2001");
       normalContext.setAmount(999.9);
       ServiceContext normalResult = gateway.orchestrateOrderFlow(normalContext);
       System.out.println("最终结果:" + normalResult.getResult());
      
       // 3. 测试异常订单流程(金额非法)
       System.out.println("\n======= 测试异常订单流程(金额非法) =======");
       ServiceContext errorContext = new ServiceContext();
       errorContext.setUserId("U1002");
       errorContext.setOrderId("O2002");
       errorContext.setAmount(-100); // 非法金额
       ServiceContext errorResult = gateway.orchestrateOrderFlow(errorContext);
       System.out.println("最终结果:" + errorResult.getErrorMsg());
      
       context.close();

      }
      }

输出结果

复制代码
【中介者-API网关】注册服务:USER_SERVICE
【中介者-API网关】注册服务:ORDER_SERVICE
【中介者-API网关】注册服务:PAY_SERVICE

======= 测试正常订单流程 =======
【中介者-API网关】开始编排订单流程,订单ID:O2001
【中介者-API网关】转发请求到 USER_SERVICE,用户ID:U1001,订单ID:O2001
【同事类-用户服务】校验用户:U1001
【中介者-API网关】转发请求到 ORDER_SERVICE,用户ID:U1001,订单ID:O2001
【同事类-订单服务】创建订单:O2001,金额:999.9
【中介者-API网关】转发请求到 PAY_SERVICE,用户ID:U1001,订单ID:O2001
【同事类-支付服务】支付订单:O2001,金额:999.9
【中介者-API网关】订单流程完成,订单ID:O2001
最终结果:订单流程完成:用户校验通过→订单创建成功→支付成功,订单ID:O2001

======= 测试异常订单流程(金额非法) =======
【中介者-API网关】开始编排订单流程,订单ID:O2002
【中介者-API网关】转发请求到 USER_SERVICE,用户ID:U1002,订单ID:O2002
【同事类-用户服务】校验用户:U1002
【中介者-API网关】转发请求到 ORDER_SERVICE,用户ID:U1002,订单ID:O2002
【同事类-订单服务】创建订单:O2002,金额:-100.0
【中介者-API网关】创建订单失败:订单金额非法:-100.0
最终结果:订单金额非法:-100.0

四、中介者模式的核心特点与适用场景

优点

  1. 解耦组件依赖:组件间无需直接引用,所有交互通过中介者完成,降低耦合度;

  2. 集中管理交互逻辑:复杂的组件交互规则统一在中介者中实现,避免逻辑分散;

  3. 提升扩展性:新增组件时,只需修改中介者的注册 / 转发逻辑,无需修改原有组件;

  4. 简化维护:交互逻辑集中,定位问题、修改规则更高效;

  5. 支持多对多交互:中介者可轻松处理多个组件间的复杂通信(如群聊、服务编排)。
    缺点

  6. 中介者类膨胀:复杂系统中,中介者会封装大量交互逻辑,导致类过大、难以维护("上帝类" 问题);

  7. 性能瓶颈:所有交互都经过中介者,高并发场景下中介者可能成为性能瓶颈;

  8. 依赖集中:组件对中介者的依赖增强,中介者故障会影响所有组件;

  9. 调试复杂:交互逻辑集中在中介者,需跟踪中介者与多个组件的调用流程。
    适用场景

  10. 组件间形成网状依赖:如多个 GUI 组件(按钮、文本框、下拉框)交互、多个微服务调用;

  11. 多对多通信场景:如群聊、消息队列、事件总线;

  12. 服务编排 / 流程控制:如微服务网关、订单流程(创建→支付→发货)、审批流程;

  13. 框架底层设计:如 Spring 的 ApplicationContext(作为 Bean 之间的中介者)、Netty 的 EventLoop(作为 Channel 的中介者);

  14. 需要集中管控交互:如权限校验、日志记录、熔断降级(中介者统一处理)。

五、JDK/ Spring 中的原生应用(必须知道)

中介者模式在框架中应用广泛,以下是核心场景:

  1. Spring 核心容器(ApplicationContext)
  • 抽象中介者:ApplicationContext;
  • 具体中介者:AnnotationConfigApplicationContext、ClassPathXmlApplicationContext;
  • 同事类:所有 Spring Bean;
  • 核心逻辑:Bean 之间不直接依赖,而是通过 ApplicationContext 获取依赖(依赖注入),ApplicationContext 协调 Bean 的创建、初始化、销毁。
  1. Spring Cloud 网关(Spring Cloud Gateway/Zuul)
  • 抽象中介者:GatewayFilter/RouteLocator;
  • 具体中介者:DispatcherHandler(网关核心处理器);
  • 同事类:各微服务;
  • 核心逻辑:网关作为中介者,转发请求、协调服务调用、处理跨域 / 限流 / 熔断。
  1. Java AWT/Swing(GUI 组件)
  • 抽象中介者:java.awt.EventQueue;
  • 具体中介者:javax.swing.DefaultButtonModel;
  • 同事类:按钮、文本框、下拉框等 GUI 组件;
  • 核心逻辑:GUI 组件不直接交互,而是通过事件队列(中介者)处理用户操作。
  1. 消息中间件(RabbitMQ/Kafka)
  • 抽象中介者:Exchange(交换机);
  • 具体中介者:DirectExchange/TopicExchange;
  • 同事类:生产者、消费者;
  • 核心逻辑:生产者 / 消费者不直接通信,而是通过交换机(中介者)转发消息。
  1. Netty 事件循环(EventLoop)
  • 抽象中介者:EventLoop;
  • 具体中介者:NioEventLoop;
  • 同事类:Channel/Handler;
  • 核心逻辑:EventLoop 作为中介者,协调 Channel 的 I/O 事件、Handler 的执行。

六、中介者模式 vs 观察者模式

维度 中介者模式 观察者模式
核心目的 协调多个组件间的双向交互 实现一对多的单向通知
核心结构 中介者 + 多个同事类(星型依赖) 主题 + 多个观察者(一对多依赖)
交互方向 双向(组件→中介者→组件) 单向(主题→观察者)
触发方式 组件主动调用中介者 主题主动通知观察者
典型场景 群聊、服务网关、GUI 组件交互 事件通知、消息推送、状态监听

总结

  1. 中介者模式的核心是用一个中介者封装组件间的交互,将网状依赖转为星型依赖,降低组件耦合度;
  2. 核心角色包括抽象中介者(交互规范)、具体中介者(核心交互逻辑)、抽象同事类(组件规范)、具体同事类(业务组件);
  3. 业务开发中,中介者模式最实用的场景是微服务网关、服务编排、群聊 / 消息转发,需注意避免中介者类过度膨胀;
  4. 中介者模式的优点是解耦、集中管理交互,缺点是可能形成 "上帝类",需合理拆分中介者职责(如按功能拆分多个中介者)。
相关推荐
Anurmy2 小时前
设计模式之抽象工厂
设计模式
鸽鸽程序猿2 小时前
【JavaEE】【SpringAI】聊天模型
java·java-ee
韭菜张师傅2 小时前
Ceph环境完全重置指南:彻底清理集群环境
java·网络·ceph
SunnyDays10112 小时前
使用 Java 实现 Word 文档水印自动化(全面指南)
java·添加水印·word文档
敲代码的小王!2 小时前
prompt开发游戏-哄哄模拟器
java·游戏·ai·prompt
学编程就要猛2 小时前
JavaEE:多线程初阶
java·开发语言·jvm
筱顾大牛2 小时前
缓存更新策略
java·redis·缓存
sheji34162 小时前
【开题答辩全过程】以 慧医疗网上医院管理系统为例,包含答辩的问题和答案
java
子一!!2 小时前
JavaEE初阶第一课时==计算机与系统讨论==
java·java-ee