责任链设计模式
什么是责任链?
责任链设计模式(Chain of Responsibility Pattern)是一种行为型设计模式,用于将请求的发送者与接收者解耦,使多个对象都有机会处理该请求,从而避免请求的发送者与接收者之间的耦合关系。这个模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
✅ 一句话理解:
请求在链条上依次传递,直到被某个处理者处理。
📌 模式结构:
责任链模式通常包含以下角色:
- Handler(抽象处理者) 定义一个处理请求的接口,并包含一个指向下一个处理者的引用。
- ConcreteHandler(具体处理者) 实现处理请求的具体逻辑。如果自己能处理,就处理;否则转交给下一个处理者。
- Client(客户端) 创建处理链,并将请求提交到链上。
🎯 使用场景:
- 多个对象可以处理同一个请求,具体由运行时决定。
- 想要动态指定处理请求的顺序。
- 不明确请求的接收者是谁时。
- 需要解耦请求发送者与多个处理者的关系时。
⚠️ 注意事项:
- 责任链太长可能影响性能。
- 链中某个节点如果没有处理请求也不传递下去,可能导致请求丢失。
代码实操详解
举一个在物流项目中的例子

在上述的流程图中可以看出,计算运费的第一步逻辑就是需要查找到对应的运费模板,否则不能进行计算,如何实现比较好呢,我们这里采用【责任链】模式进行代码编写。
之所以采用【责任链】模式,是因为在查找模板时,不同的模板处理逻辑不同,并且这些逻辑组成了一条处理链,有开头有结尾,只要能找到符合条件的模板即结束。
抽象类
首先,定义运费模板处理链,这是一个抽象类:
package com.sl.ms.carriage.handler;
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;
}
}具体类
下面针对不同的模板策略编写具体的实现类,同城寄:
package com.sl.ms.carriage.handler;
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(100) //定义顺序
@Component
public class SameCityChainHandler extends AbstractCarriageChainHandler {
@Resource
private CarriageService carriageService;
@Override
public CarriageEntity doHandler(WaybillDTO waybillDTO) {
CarriageEntity carriageEntity = null;
if (ObjectUtil.equals(waybillDTO.getReceiverCityId(), waybillDTO.getSenderCityId())) {
//同城
carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.SAME_CITY);
}
return doNextHandler(waybillDTO, carriageEntity);
}
}省内寄:
package com.sl.ms.carriage.handler;
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.handler;
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);
}
}跨省寄:
package com.sl.ms.carriage.handler;
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);
}
}客户端
组装处理链,按照@Order注解中的值,由小到大排序。
package com.sl.ms.carriage.handler;
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注解
*/
@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);
}
}测试:
package com.sl.ms.carriage.handler;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class CarriageChainHandlerTest {
@Resource
private CarriageChainHandler carriageChainHandler;
@Test
void findCarriage() {
WaybillDTO waybillDTO = WaybillDTO.builder()
.senderCityId(2L) //北京
.receiverCityId(161793L) //上海
.volume(1)
.weight(1d)
.build();
CarriageEntity carriage = this.carriageChainHandler.findCarriage(waybillDTO);
System.out.println(carriage);
}
}