责任链模式应该怎么用

xiaojiuaigc@163.com 发布于 2025-04-14 314 次阅读


责任链模式是一种行为模式,把多个处理器组成一条链,但具体由哪个处理器来处理,根据条件判断来确定,如果不能处理会传递给该链中的下一个处理器,直到有处理器处理它为止。

有这样一个需求计算费用模板

运费模板有4种类型,分别为:
同城寄:同城寄件运费计算模板,全国统一定价
省内寄:省内寄件运费计算模板,全国统一定价
跨省寄:不同省份间的运费计算模板,全国统一定价
经济区互寄:4个经济区(京津翼、江沪浙皖、川渝、黑吉辽),经济区间寄件可设置优惠价格

在查找费用的时候需要先查询是否是同城,不是继续查询是否是省内互寄,还不是查询是否是经济区互寄都不是的话用跨省来兜底

在一开始代码是这样写的

Code Example
/**
 * 根据参数查询运费模板
 *
 * @param waybillDTO 运费计算对象
 * @return 运费模板
 */
private CarriageEntity findCarriage(WaybillDTO waybillDTO) {
    // 判断是否是同城互寄
    if (ObjectUtil.equals(waybillDTO.getSenderCityId(), waybillDTO.getReceiverCityId())) {
        // 同城
        CarriageEntity carriageEntity = this.findByTemplateType(CarriageConstant.SAME_CITY);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity;
        }
    }
    // 判断是否是省内寄
    Long senderProvinceId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();
    Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
    if (ObjectUtil.equals(senderProvinceId, receiverProvinceId)) {
        // 省内寄件
        CarriageEntity carriageEntity = this.findByTemplateType(CarriageConstant.SAME_PROVINCE);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity;
        }
    }
    // 3.1 获取经济区城市配置枚举
    LinkedHashMap<String, EconomicRegionEnum> EconomicRegionMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
    EconomicRegionEnum economicRegionEnum = null;
    for (EconomicRegionEnum regionEnum : EconomicRegionMap.values()) {
        // 该经济区是否全部包含收发件省id
        boolean result = ArrayUtil.containsAll(regionEnum.getValue(), receiverProvinceId, senderProvinceId);
        if (result) {
            economicRegionEnum = regionEnum;
            break;
        }
    }
    if (ObjectUtil.isNotEmpty(economicRegionEnum)) {
        // 3.2 根据类型编码查询
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers
            .lambdaQuery(CarriageEntity.class)
            .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
            .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
            .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());

        CarriageEntity carriageEntity = super.getOne(queryWrapper);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity;
        }
    }
    // 都不是跨省模板
    return this.findByTemplateType(CarriageConstant.TRANS_PROVINCE);
}

/**
 * 根据模板类型查询模板,经济区互寄不通过该方法查询模板
 *
 * @param templateType 模板类型:1-同城寄,2-省内寄,4-跨省
 * @return 运费模板
 */
@Override
public CarriageEntity findByTemplateType(Integer templateType) {
    // 排除经济区互寄
    if (ObjectUtil.equals(templateType, CarriageConstant.ECONOMIC_ZONE)) {
        throw new SLException(CarriageExceptionEnum.METHOD_CALL_ERROR);
    }
    // 根据条件查询模板
    LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
        .eq(CarriageEntity::getTemplateType, templateType)
        .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST);
    return super.getOne(queryWrapper);
}
        

这样写代码较为臃肿,可读性比较差且难以维护,在添加新的模板的时候需要修改原有的代码,违反开闭原则。所以我们引入了责任链

首先定义一个运费模板处理链的抽象类

AbstractCarriageChainHandler Code
package com.sl.ms.carriage.hander;

import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;

/**
 * 运费模板处理链的抽象定义
 */
public abstract class AbstractCarriageChainHandler {

    private AbstractCarriageChainHandler nextHandler;

    /**
     * 执行过滤方法,通过输入参数查找运费模板
     *
     * @param waybillDTO 输入参数
     * @return 运费模板
     */
    public abstract CarriageEntity doHandler(WaybillDTO waybillDTO);

    /**
     * 执行下一个处理器
     *
     * @param waybillDTO 输入参数
     * @param carriageEntity 上个handler处理得到的对象
     * @return
     */
    protected CarriageEntity doNextHandler(WaybillDTO waybillDTO, CarriageEntity carriageEntity) {
        if (nextHandler == null || carriageEntity != null) {
            // 如果下游Handler为空 或 上个Handler已经找到运费模板就返回
            return carriageEntity;
        }
        return nextHandler.doHandler(waybillDTO);
    }

    /**
     * 设置下游Handler
     *
     * @param nextHandler 下游Handler
     */
    public void setNextHandler(AbstractCarriageChainHandler nextHandler) {
        this.nextHandler = nextHandler;
    }
}
        

然后定义四个模板的排序规则以及处理器

SameCityChainHandler Code
package com.sl.ms.carriage.hander;

import cn.hutool.core.util.ObjectUtil;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.service.CarriageService;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/*
 * 同城处理器
 * @Author xiaojiu
 * @Date 2025/4/14 9:39
 */
@Component
@Order(100) // 定义顺序
public class SameCityChainHandler extends AbstractCarriageChainHandler {
    @Resource
    private CarriageService carriageService;

    /**
     * 执行过滤方法,通过输入参数查找运费模板
     *
     * @param waybillDTO 输入参数
     * @return 运费模板
     */
    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = null;
        // 判断是否是同城互寄
        if (ObjectUtil.equals(waybillDTO.getSenderCityId(), waybillDTO.getReceiverCityId())) {
            // 同城
            carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.SAME_CITY);
        }
        return super.doNextHandler(waybillDTO, carriageEntity);
    }
}
        
SameProvinceChainHandler Code
package com.sl.ms.carriage.hander;

import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.transport.common.util.ObjectUtil;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 省内寄
 */
@Order(200) // 定义顺序
@Component
public class SameProvinceChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;

    @Resource
    private AreaFeign areaFeign;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = null;
        // 获取收寄件地址省份id
        Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
        Long senderProvinceId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();
        if (ObjectUtil.equal(receiverProvinceId, senderProvinceId)) {
            // 省内
            carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.SAME_PROVINCE);
        }
        return doNextHandler(waybillDTO, carriageEntity);
    }
}
        






package com.sl.ms.carriage.hander;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.domain.enums.EconomicRegionEnum;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.transport.common.util.ObjectUtil;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.LinkedHashMap;

/**
 * 经济区互寄
 */
@Order(300) // 定义顺序
@Component
public class EconomicZoneChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;

    @Resource
    private AreaFeign areaFeign;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = null;

        // 获取收寄件地址省份id
        Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
        Long senderProvinceId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();

        // 获取经济区城市配置枚举
        LinkedHashMap<String, EconomicRegionEnum> EconomicRegionMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        EconomicRegionEnum economicRegionEnum = null;
        for (EconomicRegionEnum regionEnum : EconomicRegionMap.values()) {
            // 该经济区是否全部包含收发件省id
            boolean result = ArrayUtil.containsAll(regionEnum.getValue(), receiverProvinceId, senderProvinceId);
            if (result) {
                economicRegionEnum = regionEnum;
                break;
            }
        }

        if (ObjectUtil.isNotEmpty(economicRegionEnum)) {
            // 根据类型编码查询
            LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(CarriageEntity.class)
                .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
                .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
                .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());
            carriageEntity = this.carriageService.getOne(queryWrapper);
        }

        return doNextHandler(waybillDTO, carriageEntity);
    }
}
        

 

TransProvinceChainHandler Code
package com.sl.ms.carriage.hander;

import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.service.CarriageService;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 跨省
 */
@Order(400) // 定义顺序
@Component
public class TransProvinceChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.TRANS_PROVINCE);
        return doNextHandler(waybillDTO, carriageEntity);
    }
}
        
CarriageChainHandler Code
package com.sl.ms.carriage.hander;

import cn.hutool.core.collection.CollUtil;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.transport.common.exception.SLException;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;

/*
 * 查找运费模板处理链 @Order指定hander顺序
 * @Author xiaojiu
 * @Date 2025/4/14 9:52
 */
@Component
public class CarriageChainHandler {
    /**
     * 使用Spring注入的顺序,按照@Order注解的顺序注入进来
     */
    @Resource
    private List<AbstractCarriageChainHandler> chainHandlers;

    private AbstractCarriageChainHandler firstHandler;

    /**
     * 组装处理链
     */
    @PostConstruct
    private void constructChain() {
        if (CollUtil.isEmpty(chainHandlers)) {
            throw new SLException("not found carriage chain handler!");
        }
        // 处理器的第一个节点
        firstHandler = chainHandlers.get(0);
        for (int i = 0; i < chainHandlers.size(); i++) {
            if (i == chainHandlers.size() - 1) {
                // 最后一个处理链节点
                chainHandlers.get(i).setNextHandler(null);
            } else {
                // 设置下游节点
                chainHandlers.get(i).setNextHandler(chainHandlers.get(i + 1));
            }
        }
    }

    public CarriageEntity findCarriage(WaybillDTO waybillDTO) {
        // 从第一个节点开始处理
        return firstHandler.doHandler(waybillDTO);
    }
}
        

这样也就完成了责任链模板的定义以后在添加新的模板的时候只需要定义一个模板处理器和顺序就能轻松实现

但责任链模板也有一定的缺陷,当责任链过长的时候查找的内容过多,可能会导致性能损耗,我们可以引入redis来解决。本业务只有四个模板算不上多。如果后期模板真的多了再加redis.