中介者模式:设计与实践

34 阅读18分钟

中介者模式:设计与实践

一、什么是中介者模式

1. 基本定义

中介者模式(Mediator Pattern)是一种行为型设计模式,由《设计模式:可复用面向对象软件的基础》(GOF著作)定义为:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互

该模式通过引入一个中介者对象,将多个对象(同事类)之间的交互集中管理,同事类之间不再直接通信,而是通过中介者传递信息。核心是将“多对多”的复杂交互关系转化为“一对多”的简单关系,降低系统耦合度。

2. 核心思想

中介者模式的核心在于集中协调与解耦交互。当系统中存在多个对象需要相互通信时,对象间的直接引用会形成复杂的网状结构,导致系统难以维护和扩展。通过引入中介者,所有对象的交互都由中介者统一处理,对象只需关注自身业务逻辑,无需关心其他对象的存在,从而实现对象间的解耦,提高系统的灵活性和可维护性。

二、中介者模式的特点

1. 集中交互逻辑

所有对象间的交互逻辑被集中在中介者中,避免分散在各个对象中,便于统一管理和修改。

2. 减少直接依赖

同事类之间不直接引用,仅通过中介者通信,降低对象间的耦合度,形成“星形”依赖结构。

3. 简化对象职责

同事类只需关注自身业务逻辑,无需处理与其他对象的交互,职责更单一。

4. 易于扩展

新增交互规则只需修改中介者,新增同事类只需在中介者中添加相应处理逻辑,符合开闭原则。

5. 可能导致中介者膨胀

若系统中对象过多或交互复杂,中介者可能会变得庞大,承担过多职责,需要注意拆分。

特点说明
集中交互逻辑交互规则集中在中介者,避免分散
减少直接依赖同事类通过中介者通信,无直接引用
简化对象职责同事类专注自身业务,不处理交互
易于扩展新增规则或对象只需修改中介者
可能导致中介者膨胀复杂场景下中介者可能过于庞大

三、中介者模式的标准代码实现

1. 模式结构

中介者模式包含四个核心角色:

  • 抽象中介者(Mediator):定义中介者与同事类交互的接口,声明协调同事类的方法。
  • 具体中介者(ConcreteMediator):实现抽象中介者接口,持有所有同事类的引用,负责协调同事类的交互。
  • 抽象同事类(Colleague):定义同事类的接口,持有抽象中介者的引用,提供与中介者通信的方法。
  • 具体同事类(ConcreteColleague):实现抽象同事类接口,完成自身业务逻辑,通过中介者与其他同事类交互。

2. 代码实现示例

2.1 抽象中介者与同事类
/**
 * 抽象中介者
 */
public interface Mediator {
    /**
     * 注册同事类
     */
    void register(Colleague colleague);

    /**
     * 转发消息
     */
    void relay(Colleague sender, String message);
}

/**
 * 抽象同事类
 */
public abstract class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    /**
     * 发送消息
     */
    public abstract void send(String message);

    /**
     * 接收消息
     */
    public abstract void receive(String message);
}
2.2 具体中介者与同事类
import java.util.ArrayList;
import java.util.List;

/**
 * 具体中介者
 */
public class ConcreteMediator implements Mediator {
    private List<Colleague> colleagues = new ArrayList<>();

    @Override
    public void register(Colleague colleague) {
        colleagues.add(colleague);
    }

    @Override
    public void relay(Colleague sender, String message) {
        // 转发消息给除发送者外的其他同事
        for (Colleague colleague : colleagues) {
            if (colleague != sender) {
                colleague.receive(message);
            }
        }
    }
}

/**
 * 具体同事类A
 */
public class ConcreteColleagueA extends Colleague {
    public ConcreteColleagueA(Mediator mediator) {
        super(mediator);
    }

    @Override
    public void send(String message) {
        System.out.println("同事A发送消息:" + message);
        mediator.relay(this, message);
    }

    @Override
    public void receive(String message) {
        System.out.println("同事A收到消息:" + message);
    }
}

/**
 * 具体同事类B
 */
public class ConcreteColleagueB extends Colleague {
    public ConcreteColleagueB(Mediator mediator) {
        super(mediator);
    }

    @Override
    public void send(String message) {
        System.out.println("同事B发送消息:" + message);
        mediator.relay(this, message);
    }

    @Override
    public void receive(String message) {
        System.out.println("同事B收到消息:" + message);
    }
}
2.3 客户端使用示例
/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 创建中介者
        Mediator mediator = new ConcreteMediator();

        // 创建同事类并注册到中介者
        Colleague colleagueA = new ConcreteColleagueA(mediator);
        Colleague colleagueB = new ConcreteColleagueB(mediator);
        mediator.register(colleagueA);
        mediator.register(colleagueB);

        // 同事类通过中介者通信
        System.out.println("=== 同事A发送消息 ===");
        colleagueA.send("请确认当前状态");

        System.out.println("\n=== 同事B发送消息 ===");
        colleagueB.send("状态正常,可以执行操作");
    }
}

3. 代码实现特点总结

角色核心职责代码特点
抽象中介者(Mediator)定义中介者与同事类的交互接口声明register()(注册同事)和relay()(转发消息)等方法
具体中介者(ConcreteMediator)实现中介逻辑,协调同事类交互持有同事类集合,relay()方法中实现消息转发或交互规则
抽象同事类(Colleague)定义同事类接口,持有中介者引用包含mediator字段,声明send()receive()方法
具体同事类(ConcreteColleague)实现自身业务,通过中介者交互实现send()(调用中介者转发)和receive()(处理收到的消息)方法

四、支付框架设计中中介者模式的运用

支付场景化营销协同场景为例,说明中介者模式在支付系统中的具体应用:

1. 场景分析

支付过程中,用户可能享受多种营销优惠(如优惠券、积分抵扣、银行补贴、满减活动),涉及多个营销组件的协同:

  • 优惠券服务:验证并扣减可用优惠券
  • 积分服务:查询用户积分并抵扣金额
  • 银行活动服务:检查是否符合银行满减条件(如招行信用卡满100减10)
  • 满减规则服务:判断订单金额是否满足满减条件(如满200减30)
  • 价格计算服务:综合所有优惠后计算最终支付金额

若这些组件直接交互,会形成复杂的依赖关系(如优惠券服务需调用积分服务判断是否可叠加使用),新增优惠类型(如会员折扣)时需修改多个组件。使用中介者模式可通过营销中介者统一协调各组件,实现优惠规则的集中管理。

2. 设计实现

2.1 抽象中介者与同事类
import java.math.BigDecimal;

/**
 * 营销中介者接口
 */
public interface MarketingMediator {
    /**
     * 注册营销组件
     */
    void register(MarketingColleague colleague);

    /**
     * 协调计算优惠后金额
     */
    BigDecimal calculateFinalAmount(PaymentContext context);

    /**
     * 应用所有优惠(扣减优惠券、积分等)
     */
    void applyAllPromotions(PaymentContext context);
}

/**
 * 营销同事类接口
 */
public abstract class MarketingColleague {
    protected MarketingMediator mediator;
    protected String promotionType; // 优惠类型(如COUPON、POINT、BANK)

    public MarketingColleague(MarketingMediator mediator, String promotionType) {
        this.mediator = mediator;
        this.promotionType = promotionType;
    }

    /**
     * 计算本组件可抵扣的金额
     */
    public abstract BigDecimal calculateDeduction(PaymentContext context);

    /**
     * 应用本组件的优惠(如扣减优惠券)
     */
    public abstract void applyPromotion(PaymentContext context);

    public String getPromotionType() {
        return promotionType;
    }
}
2.2 具体营销组件(同事类)
/**
 * 优惠券组件
 */
public class CouponColleague extends MarketingColleague {
    public CouponColleague(MarketingMediator mediator) {
        super(mediator, "COUPON");
    }

    @Override
    public BigDecimal calculateDeduction(PaymentContext context) {
        // 验证并计算可抵扣的优惠券金额
        String couponId = context.getCouponId();
        if (couponId == null || couponId.isEmpty()) {
            return BigDecimal.ZERO;
        }
        // 实际中会调用优惠券服务查询面额并验证有效性
        return new BigDecimal("20"); // 假设优惠券面额20元
    }

    @Override
    public void applyPromotion(PaymentContext context) {
        // 扣减优惠券(调用优惠券服务标记为已使用)
        System.out.printf("应用优惠券%s,抵扣金额:%s%n",
                context.getCouponId(), calculateDeduction(context));
    }
}

/**
 * 积分组件
 */
public class PointColleague extends MarketingColleague {
    public PointColleague(MarketingMediator mediator) {
        super(mediator, "POINT");
    }

    @Override
    public BigDecimal calculateDeduction(PaymentContext context) {
        // 计算积分可抵扣金额(100积分=1元)
        int points = context.getUserPoints(); // 从上下文获取用户积分
        return new BigDecimal(points).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
    }

    @Override
    public void applyPromotion(PaymentContext context) {
        BigDecimal deduction = calculateDeduction(context);
        int pointsUsed = deduction.multiply(new BigDecimal("100")).intValue();
        System.out.printf("使用积分%d,抵扣金额:%s%n", pointsUsed, deduction);
        // 调用积分服务扣减积分
    }
}

/**
 * 银行活动组件
 */
public class BankActivityColleague extends MarketingColleague {
    public BankActivityColleague(MarketingMediator mediator) {
        super(mediator, "BANK");
    }

    @Override
    public BigDecimal calculateDeduction(PaymentContext context) {
        // 检查银行活动(如招行信用卡满100减10)
        String bankCardType = context.getBankCardType();
        BigDecimal amount = context.getOriginalAmount();
        if ("CMB_CREDIT".equals(bankCardType) && amount.compareTo(new BigDecimal("100")) >= 0) {
            return new BigDecimal("10"); // 满100减10
        }
        return BigDecimal.ZERO;
    }

    @Override
    public void applyPromotion(PaymentContext context) {
        BigDecimal deduction = calculateDeduction(context);
        if (deduction.compareTo(BigDecimal.ZERO) > 0) {
            System.out.printf("应用银行优惠,抵扣金额:%s%n", deduction);
            // 通知银行活动服务记录优惠使用
        }
    }
}

/**
 * 满减规则组件
 */
public class FullReductionColleague extends MarketingColleague {
    public FullReductionColleague(MarketingMediator mediator) {
        super(mediator, "FULL_REDUCTION");
    }

    @Override
    public BigDecimal calculateDeduction(PaymentContext context) {
        // 满减规则:满200减30,满500减80
        BigDecimal amount = context.getOriginalAmount();
        if (amount.compareTo(new BigDecimal("500")) >= 0) {
            return new BigDecimal("80");
        } else if (amount.compareTo(new BigDecimal("200")) >= 0) {
            return new BigDecimal("30");
        }
        return BigDecimal.ZERO;
    }

    @Override
    public void applyPromotion(PaymentContext context) {
        BigDecimal deduction = calculateDeduction(context);
        if (deduction.compareTo(BigDecimal.ZERO) > 0) {
            System.out.printf("应用满减优惠,抵扣金额:%s%n", deduction);
        }
    }
}
2.3 具体中介者实现
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 营销中介者实现类
 */
public class MarketingMediatorImpl implements MarketingMediator {
    private List<MarketingColleague> colleagues = new ArrayList<>();

    @Override
    public void register(MarketingColleague colleague) {
        colleagues.add(colleague);
    }

    @Override
    public BigDecimal calculateFinalAmount(PaymentContext context) {
        BigDecimal originalAmount = context.getOriginalAmount();
        BigDecimal totalDeduction = BigDecimal.ZERO;

        // 1. 计算所有优惠的总抵扣金额
        for (MarketingColleague colleague : colleagues) {
            BigDecimal deduction = colleague.calculateDeduction(context);
            totalDeduction = totalDeduction.add(deduction);
        }

        // 2. 确保抵扣后金额不小于0
        BigDecimal finalAmount = originalAmount.subtract(totalDeduction);
        if (finalAmount.compareTo(BigDecimal.ZERO) < 0) {
            finalAmount = BigDecimal.ZERO;
        }

        // 3. 保存计算结果到上下文
        context.setTotalDeduction(totalDeduction);
        context.setFinalAmount(finalAmount);
        return finalAmount;
    }

    @Override
    public void applyAllPromotions(PaymentContext context) {
        // 按优先级应用优惠(如先满减,再优惠券,最后积分)
        for (MarketingColleague colleague : getSortedColleagues()) {
            colleague.applyPromotion(context);
        }
        // 输出最终结果
        System.out.printf("原价:%s,总优惠:%s,实付金额:%s%n",
                context.getOriginalAmount(),
                context.getTotalDeduction(),
                context.getFinalAmount());
    }

    // 按优先级排序营销组件
    private List<MarketingColleague> getSortedColleagues() {
        // 实际中可通过配置定义优先级,这里简化为固定顺序
        List<MarketingColleague> sorted = new ArrayList<>();
        for (MarketingColleague c : colleagues) {
            if ("FULL_REDUCTION".equals(c.getPromotionType())) sorted.add(0, c);
            else if ("COUPON".equals(c.getPromotionType())) sorted.add(1, c);
            else if ("BANK".equals(c.getPromotionType())) sorted.add(2, c);
            else if ("POINT".equals(c.getPromotionType())) sorted.add(3, c);
        }
        return sorted;
    }
}
2.4 上下文与客户端使用
import java.math.BigDecimal;

/**
 * 支付上下文(存储支付相关信息)
 */
public class PaymentContext {
    private String orderId;
    private BigDecimal originalAmount; // 原始金额
    private String userId;
    private String couponId; // 优惠券ID
    private int userPoints; // 用户积分
    private String bankCardType; // 银行卡类型
    private BigDecimal totalDeduction; // 总优惠金额
    private BigDecimal finalAmount; // 最终支付金额

    // getter和setter方法...
}

/**
 * 支付服务(客户端)
 */
public class PaymentService {
    public void processPayment() {
        // 1. 创建上下文并设置支付信息
        PaymentContext context = new PaymentContext();
        context.setOrderId("ORD20240501001");
        context.setOriginalAmount(new BigDecimal("250")); // 原价250元
        context.setUserId("USER123");
        context.setCouponId("CPN886"); // 使用优惠券
        context.setUserPoints(500); // 用户有500积分(可抵扣5元)
        context.setBankCardType("CMB_CREDIT"); // 招行信用卡

        // 2. 创建营销中介者并注册组件
        MarketingMediator mediator = new MarketingMediatorImpl();
        mediator.register(new CouponColleague(mediator));
        mediator.register(new PointColleague(mediator));
        mediator.register(new BankActivityColleague(mediator));
        mediator.register(new FullReductionColleague(mediator));

        // 3. 计算最终支付金额
        mediator.calculateFinalAmount(context);

        // 4. 应用所有优惠
        mediator.applyAllPromotions(context);
    }

    public static void main(String[] args) {
        new PaymentService().processPayment();
    }
}

3. 模式价值体现

  • 集中管理优惠规则:所有优惠的计算和应用逻辑由中介者协调,避免营销组件间的直接依赖(如优惠券无需判断是否可与积分同时使用)
  • 灵活扩展新优惠:新增会员折扣只需添加MemberDiscountColleague并注册到中介者,无需修改现有组件,符合开闭原则
  • 统一优惠优先级:中介者控制优惠应用顺序(如先满减后用券),避免组件各自为政导致的规则冲突
  • 简化组件职责:各营销组件只需实现自身的优惠计算和应用逻辑,无需关心其他组件的存在,代码更简洁
  • 便于规则调整:修改优惠叠加规则(如禁止银行补贴与满减同时使用)只需修改中介者的calculateFinalAmount方法,无需改动组件

五、开源框架中中介者模式的运用

在开源框架中,Spring MVC的DispatcherServlet是中介者模式的经典实现。作为前端控制器,它集中协调请求处理流程中的所有核心组件,避免组件间形成直接依赖,完美体现了"集中交互、解耦依赖"的设计思想。

1. DispatcherServlet的中介者角色定位

Spring MVC的请求处理涉及多个核心组件(同事类):

  • HandlerMapping:根据请求查找对应的处理器(Handler)
  • HandlerAdapter:适配不同类型的处理器(如注解式Controller、HttpRequestHandler)
  • HandlerExceptionResolver:处理请求过程中的异常
  • ViewResolver:将逻辑视图名解析为具体视图对象
  • ModelAndView:封装处理器返回的模型数据和视图信息

DispatcherServlet作为中介者,承担以下核心职责:

  • 接收客户端所有请求,作为唯一入口
  • 协调各组件按序完成请求处理(路由→适配→执行→异常处理→视图渲染)
  • 维护组件间的交互规则,屏蔽组件间的直接依赖

2. 核心代码实现分析

以下基于Spring MVC源码简化实现,重点展示DispatcherServlet如何作为中介者协调各组件:

2.1 抽象中介者与同事类接口

Spring MVC通过接口定义组件交互规范(类似抽象中介者和抽象同事类):

// 处理器映射接口(同事类)
public interface HandlerMapping {
    // 根据请求查找处理器
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

// 处理器适配器接口(同事类)
public interface HandlerAdapter {
    // 判断是否支持当前处理器
    boolean supports(Object handler);
    // 执行处理器,返回模型和视图
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

// 视图解析器接口(同事类)
public interface ViewResolver {
    // 解析视图名
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

// 异常处理器接口(同事类)
public interface HandlerExceptionResolver {
    // 处理异常
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, 
                                 Object handler, Exception ex);
}
2.2 具体中介者:DispatcherServlet

DispatcherServlet作为具体中介者,持有所有同事类的引用,并实现核心协调逻辑:

public class DispatcherServlet extends HttpServlet {
    // 同事类引用集合
    private List<HandlerMapping> handlerMappings;
    private List<HandlerAdapter> handlerAdapters;
    private List<HandlerExceptionResolver> handlerExceptionResolvers;
    private List<ViewResolver> viewResolvers;

    // 初始化:从Spring容器中获取所有组件(同事类)
    @Override
    protected void initStrategies(ApplicationContext context) {
        // 初始化处理器映射器
        handlerMappings = context.getBeansOfType(HandlerMapping.class).values().stream()
                .collect(Collectors.toList());
        // 初始化处理器适配器
        handlerAdapters = context.getBeansOfType(HandlerAdapter.class).values().stream()
                .collect(Collectors.toList());
        // 初始化异常处理器
        handlerExceptionResolvers = context.getBeansOfType(HandlerExceptionResolver.class).values().stream()
                .collect(Collectors.toList());
        // 初始化视图解析器
        viewResolvers = context.getBeansOfType(ViewResolver.class).values().stream()
                .collect(Collectors.toList());
    }

    // 核心方法:处理所有请求
    @Override
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 1. 中介者协调第一步:通过HandlerMapping查找处理器
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 2. 中介者协调第二步:通过HandlerAdapter适配处理器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 3. 中介者协调第三步:执行处理器(通过适配器)
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        } catch (Exception ex) {
            // 4. 中介者协调第四步:异常处理
            dispatchException = ex;
            mv = processHandlerException(processedRequest, response, mappedHandler, ex);
        }

        // 5. 中介者协调第五步:渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }

    // 查找处理器(协调HandlerMapping)
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : handlerMappings) {
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

    // 获取处理器适配器(协调HandlerAdapter)
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        for (HandlerAdapter ha : handlerAdapters) {
            if (ha.supports(handler)) {
                return ha;
            }
        }
        throw new ServletException("No adapter for handler");
    }

    // 处理异常(协调HandlerExceptionResolver)
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                                                  Object handler, Exception ex) throws Exception {
        for (HandlerExceptionResolver resolver : handlerExceptionResolvers) {
            ModelAndView mv = resolver.resolveException(request, response, handler, ex);
            if (mv != null) {
                return mv;
            }
        }
        throw ex; // 未处理的异常
    }

    // 渲染视图(协调ViewResolver)
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                      HandlerExecutionChain mappedHandler, ModelAndView mv,
                                      Exception exception) throws Exception {
        if (mv != null && !mv.wasCleared()) {
            // 解析视图
            View view = resolveViewName(mv.getViewName(), mv.getModelInternal(), 
                                       LocaleContextHolder.getLocale(), request);
            // 渲染视图
            view.render(mv.getModelInternal(), request, response);
        }
    }

    // 解析视图名
    private View resolveViewName(String viewName, Map<String, Object> model, 
                                Locale locale, HttpServletRequest request) throws Exception {
        for (ViewResolver viewResolver : viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }
}
2.3 同事类具体实现(示例)

以最常用的注解式处理器相关组件为例,展示同事类的实现:

// 具体同事类:RequestMappingHandlerMapping(处理@RequestMapping注解)
public class RequestMappingHandlerMapping implements HandlerMapping {
    @Override
    public HandlerExecutionChain getHandler(HttpServletRequest request) {
        // 根据请求路径匹配@Controller中的@RequestMapping方法
        String path = request.getRequestURI();
        Object handler = findHandlerByPath(path); // 内部逻辑:匹配处理器
        return new HandlerExecutionChain(handler);
    }
}

// 具体同事类:RequestMappingHandlerAdapter(适配注解式处理器)
public class RequestMappingHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        // 支持带@RequestMapping的方法处理器
        return handler instanceof HandlerMethod && 
               ((HandlerMethod) handler).hasMethodAnnotation(RequestMapping.class);
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 执行处理器方法(如Controller的方法)
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Object result = handlerMethod.invoke(); // 反射调用方法
        return new ModelAndView("viewName", "model", result); // 封装结果
    }
}

// 具体同事类:InternalResourceViewResolver(解析JSP视图)
public class InternalResourceViewResolver implements ViewResolver {
    @Override
    public View resolveViewName(String viewName, Locale locale) {
        // 将逻辑视图名解析为JSP路径(如"index"→"/WEB-INF/views/index.jsp")
        return new InternalResourceView("/WEB-INF/views/" + viewName + ".jsp");
    }
}

3. 中介者模式在DispatcherServlet中的体现

中介者模式角色Spring MVC对应实现核心交互逻辑
抽象中介者无显式接口(通过Servlet规范定义行为)定义请求处理的抽象流程
具体中介者DispatcherServlet持有所有组件引用,协调执行请求处理全流程
抽象同事类HandlerMapping、HandlerAdapter等接口定义组件的抽象行为(如getHandler、handle)
具体同事类RequestMappingHandlerMapping、RequestMappingHandlerAdapter等实现具体业务逻辑,通过中介者交互

4. 模式价值分析

DispatcherServlet作为中介者,为Spring MVC带来以下核心价值:

  1. 彻底解耦组件依赖 各组件(HandlerMapping、HandlerAdapter等)无需知道其他组件的存在,只需专注自身职责。例如,HandlerMapping只需返回处理器,无需关心后续如何适配执行;ViewResolver只需解析视图,无需知道请求来源。
  2. 集中控制请求流程 所有请求处理步骤(路由→适配→执行→异常→渲染)由DispatcherServlet统一调度,流程清晰可控。新增步骤(如请求日志记录)只需修改中介者,无需改动组件。
  3. 灵活扩展组件 新增处理器类型(如响应式处理器)只需实现对应的HandlerAdapter;新增视图类型(如JSON、Excel)只需实现ViewResolver,无需修改DispatcherServlet,完美符合开闭原则。
  4. 简化开发复杂度 开发者无需关心组件间的交互细节,只需按规范实现组件(如用@RequestMapping定义处理器),由中介者自动协调,降低使用门槛。

通过DispatcherServlet的设计可以看出,中介者模式特别适合处理"多组件协同完成复杂流程"的场景,在框架设计中能显著提升系统的灵活性和可维护性。这一思想也可直接借鉴到支付框架的跨服务协同设计中(如前文的营销中介者)。

六、总结

1. 中介者模式的适用场景

  • 当系统中存在多个对象需要频繁交互,形成复杂网状依赖时
  • 当希望集中管理对象间的交互规则,避免规则分散在各个对象中时
  • 当需要简化对象职责,使对象专注于自身业务而非交互逻辑时
  • 当需要灵活扩展交互规则,或新增交互参与者时

2. 中介者模式与其他模式的区别

  • 与观察者模式:两者都涉及对象间的通信,但观察者模式是“一对多”的通知机制(主题通知观察者),中介者模式是“多对多”的交互协调(中介者主动协调),前者是“被动通知”,后者是“主动协调”
  • 与外观模式:外观模式为子系统提供统一接口,侧重简化子系统访问,中介者模式侧重协调子系统内部对象的交互,前者是“接口封装”,后者是“交互协调”
  • 与代理模式:代理模式为单个对象提供代理,中介者模式协调多个对象的交互,前者是“对象代理”,后者是“多对象协调”

3. 支付系统中的实践价值

  • 简化复杂协同场景:支付涉及的营销、风控、渠道等组件通过中介者协调,降低系统复杂度
  • 提高可维护性:交互规则集中在中介者,便于调试和修改(如调整优惠计算逻辑)
  • 加速业务迭代:新增支付功能(如跨境支付的税费计算)只需扩展中介者,现有组件无需改动
  • 降低团队协作成本:不同组件可由不同团队开发,通过中介者接口约定交互方式,减少沟通成本

4. 实践建议

  • 避免中介者膨胀:当中介者职责过多时,可按功能拆分(如将营销中介者拆分为优惠计算中介者和优惠应用中介者)
  • 明确中介者与同事类的职责边界:中介者只负责协调,不处理具体业务逻辑;同事类只处理自身业务,不处理交互
  • 使用配置驱动规则:将交互规则(如优惠优先级)存入配置中心,中介者通过配置动态协调,避免硬编码
  • 结合事件驱动:复杂场景下,中介者可通过事件总线(如Spring Event)接收和发布事件,降低中介者与同事类的直接依赖

中介者模式通过“集中协调交互”的思想,有效解决了支付系统中多组件协同的复杂性,既降低了系统耦合度,又提高了业务扩展的灵活性,是构建复杂支付框架不可或缺的设计模式。