责任链模式
责任链模式就是一个请求能被多个对象处理,不知道具体被谁处理,就用职责链模式
1.责任链模式的本质
责任链模式的本质:分离职责,动态组合。
分离职责是前提,只有先把复杂的功能分开,拆分成很多的步骤和小的功能处理,然后才能合理规划和定义职责类。可以有很多的职责类来负责处理某一个功能,让每个职责类负责处理功能的某一个方面,在运行期间进行动态组合,形成一个处理的链,把这个链运行完,功能也就处理完了。
动态组合才是责任链模式的精华所在,因为要实现请求对象和处理对象的解耦,请求对象不知道谁才是真正的处理对象,因此要动态地把可能的处理对象组合起来。由于组合的方式是动态的,这就意味着可以很方便地修改和添加新的处理对象,从而让系统更加灵活和具有更好的扩展性。
2.何时选用责任链模式
建议在以下情况中选用责任链模式。
-
如果有多个对象可以处理同一个请求,但是具体由哪个对象来处理该请求,是运行时刻动态确定的。这种情况可以使用责任链模式,把处理请求的对象实现成为职责对象,然后把它们构成一个职责链,当请求在这个链中传递的时候,具体由哪个职责对象来处理,会在运行时动态判断。
-
当需要按照一定的顺序依次处理请求时,可以使用责任链模式。每个对象都可以根据自己的逻辑判断是否处理请求或将其传递给下一个对象,从而形成一个处理链。
-
当希望将一个请求发送给多个对象进行处理时,可以使用责任链模式。每个对象都有机会处理请求,从而实现多个对象协同处理同一个请求的目的。
-
当需要在不明确指定接收者的情况下,将请求发送给能够处理它的对象时,可以使用责任链模式。发送者只需要将请求发送给链中的第一个对象,然后由链中的对象自行决定是否处理请求或将其传递给下一个对象。
3.优缺点
责任链模式有以下优点。
-
解耦责任
责任链模式将请求的发送者和接收者解耦,发送者无需知道请求将由哪个接收者处理,接收者也无需知道请求的发送者是谁。这增强了系统的灵活性和可扩展性。 -
动态组合
责任链模式允许动态地组合和调整对象的顺序和组成,可以根据需要增加、删除或重新排序链中的对象,而不影响已有的代码。这样可以灵活地构建不同的处理链,适应不同的业务需求。
责任链模式有以下缺点。
-
可能引起性能问题
如果责任链过长或链中的对象过多,请求可能需要遍历整个链才能找到合适的处理者,这可能会影响系统的性能。 -
不一定能被处理
责任链模式的每个职责对象只负责自己处理的那一部分,因此可能会出现某个请求,把整个链传递完了,都没有职责对象处理它。这就需要在使用责任链模式的时候,需要提供默认的处理,并且注意构建的链的有效性。
4.责任链模式的结构
-
Handler:定义职责的接口,通常在这里定义处理请求的方法,可以在这里实现后继链。
-
ConcreteHandler:实现职责的类,在这个类中,实现对在它职责范围内请求的处理,如果不处理,就继续转发请求给后继者。
-
Client:职责链的客户端,向链上的具体处理对象提交请求,让职责链负责处理。
5.实现
职责链(有一个对象处理完就停止)
模拟请假流程,用户向领导提交请几天假申请,等待审批
- 项目经理审批1-3天
- 总经理审批4-7天
- 董事长审批大于7天
1.职责对象
/**
* @description:职责对象
*/
@Setter
public abstract class Handler {
/**
* 下一个处理者
*/
protected Handler successor=null;
/**
* 请假
* @param day 请假天数
* @return
*/
public abstract boolean askOff(int day);
}
2.具体职责实现类
/**
* @description:项目经理
*/
public class ProjectManager extends Handler {
@Override
public boolean askOff(int day) {
//如果假期小于3天,项目经理就可以审批,否则向下传递
if (day <= 3) {
System.out.println("项目经理审批通过,假期天数为:"+day);
return true;
} else {
if (this.successor != null) {
return successor.askOff(day);
}
}
return false;
}
}
/**
* @description:总经理
*/
public class GeneralManager extends Handler {
@Override
public boolean askOff(int day) {
//如果假期小于3天,项目经理就可以审批,否则向下传递
if (day > 3 && day <= 7) {
System.out.println("总经理审批通过,假期天数为:"+day);
return true;
} else {
if (this.successor != null) {
return successor.askOff(day);
}
}
return false;
}
}
/**
* @description:董事长
*/
public class Chairman extends Handler{
@Override
public boolean askOff(int day) {
//如果假期小于3天,项目经理就可以审批,否则向下传递
if (day > 7) {
System.out.println("董事长审批通过,假期天数为:"+day);
return true;
} else {
if (this.successor != null) {
return successor.askOff(day);
}
}
return false;
}
}
3.测试类
public class Client {
public static void main(String[] args) {
Handler h1=new ProjectManager();
Handler h2=new GeneralManager();
Handler h3=new Chairman();
//设置职责链
h1.setSuccessor(h2);
h2.setSuccessor(h3);
//请假
h1.askOff(2);
h1.askOff(5);
h1.askOff(8);
}
}
4.结果
功能链(所有对象都处理完才停止)
模拟用户登录后,修改一个数据
- 登录校验(是否登录)
- 请求参数校验(是否为空)
- 权限校验(是否有这个数据的修改权限)
1.职责对象
/**
* @description:职责对象
*/
@Setter
public abstract class Handler {
/**
* 下一个处理者
*/
protected Handler successor=null;
/**
* 校验
* @param user 用户
* @return
*/
public abstract boolean check(String user);
}
2.具体职责实现类
/**
* @description:登录校验
*/
public class LoginCheck extends Handler{
@Override
public boolean check(String user) {
//是否登录过
// if (redis.get(user)==null){
// return false;
// }
System.out.println("登录校验通过");
return this.successor.check(user);
}
}
/**
* @description:参数校验
*/
public class ParameterCheck extends Handler{
@Override
public boolean check(String user) {
//参数是否为空
if (user==null||"".equals(user)){
System.out.println("参数校验未通过");
return false;
}
System.out.println("参数校验通过");
return this.successor.check(user);
}
}
/**
* @description:权限校验
*/
public class PermissionCheck extends Handler{
@Override
public boolean check(String user) {
//是否有权限,只有张三有权限
if (!"张三".equals(user)){
System.out.println("权限校验未通过");
return false;
}
System.out.println("权限校验通过");
//所有校验都通过
return true;
}
}
3.测试类
public class Client {
public static void main(String[] args) {
Handler h1=new LoginCheck();
Handler h2=new ParameterCheck();
Handler h3=new PermissionCheck();
//设置功能链
h1.setSuccessor(h2);
h2.setSuccessor(h3);
//校验
h1.check("");
System.out.println("------------------------");
h1.check("李四");
System.out.println("------------------------");
h1.check("张三");
}
}
4.结果
拉单落库实战
现有一个拉单落库逻辑,原来的落库规则只有两种:根据商品id
或根据组合编码
落库,所有之前的实现是if-else,现增加需求,又增加了一种分销编码
落库规则,用if-else也可实现,代码可读性差;
每个平台传的参数都有可能是这三种规则的一种,不知道具体用的哪个规则匹配进行落库,所以选用责任链模式重构,可以随时动态组合
抽象类
/**
* @description:订单落库规则
*/
@Component
public abstract class StorageHandler {
@Resource
private OrderMapper orderMapper;
private StorageHandler handler;
public StorageHandler next() {
return this.handler;
}
public StorageHandler appendHandler(StorageHandler handler) {
this.handler = handler;
return this;
}
/**
* 落库
*
* @param orderDO 订单主信息
* @param detailList 订单明细信息集合
* @param storeDO 店铺信息
* @param orderDetailDto 未处理的订单信息
* @return
*/
public abstract List<OrderDetailDO> doStorage(
OrderDO orderDO,
List<OrderDetailDO> detailList,
StoreDO storeDO,
ProcessOrderHandlerDto.ProcessOrderDetailDto orderDetailDto
);
/**
* 没匹配任何规则,默认信息
*
* @param orderDO 订单主信息
* @param orderDetailDto 订单明细信息集合
* @param buyNum 购买数量
* @param amount 商品单价(单位分)
* @param totalAmount 商品总价(购买数量*商品单价)
* @return
*/
public OrderDetailDO defaultInfo(
OrderDO orderDO,
ProcessOrderHandlerDto.ProcessOrderDetailDto orderDetailDto,
Integer buyNum,
BigDecimal amount,
BigDecimal totalAmount
) {
OrderDetailDO detailDO = new OrderDetailDO();
//商品名称默认值
detailDO.setProductName(orderDetailDto.getProductName());
//商品条码默认值
detailDO.setProductBarCode(orderDetailDto.getProductBarCode());
......
return detailDO;
}
}
商品id规则
/**
* @description:商品id落库规则
*/
@Slf4j
@Component
public class ProductIDHandler extends StorageHandler {
@Autowired
ProductManageControllerFeign productManageControllerFeign;
@Override
public List<OrderDetailDO> doStorage(
OrderDO orderDO,
List<OrderDetailDO> detailList,
StoreDO storeDO,
ProcessOrderHandlerDto.ProcessOrderDetailDto orderDetailDto
) {
OrderProductInfoRpcRequest rpcRequest = new OrderProductInfoRpcRequest();
rpcRequest.setCargoOwnerId(storeDO.getCargoOwnerId());
rpcRequest.setWarehouseId(orderDO.getWarehouseId());
//查系统中商品账册信息
JsonData<OrderProductInfoVo> orderProductInfoJsonData = productManageControllerFeign
.getOrderProductInfoRPC(rpcRequest);
if (JsonData.isSuccess(orderProductInfoJsonData)) {
log.warn("订单:{},匹配到商品id落库规则",
orderDO.getOutTradeNo()
);
OrderProductInfoVo orderProductInfoVo = orderProductInfoJsonData.getData();
OrderDetailDO detailDO = new OrderDetailDO();
//商品id
detailDO.setProductId(orderProductInfoVo.getProductId());
//商品名称
detailDO.setProductName(orderProductInfoVo.getProductName());
......
return detailList;
}
StorageHandler next = super.next();
//当前为最后一个规则,即没匹配到任何规则,给默认值
if (null == next) {
log.warn("订单:{}, 当前规则:{} 没匹配任何落库规则,给默认值!!!",
orderDO.getOutTradeNo(),
"商品id"
);
OrderDetailDO detailDO = super.defaultInfo(orderDO, orderDetailDto, null, null, null);
detailList.add(detailDO);
return detailList;
}
return next.doStorage(orderDO, detailList, storeDO, orderDetailDto);
}
}
组合编码规则
/**
* @description:组合编码落库规则
*/
@Slf4j
@Component
public class GroupCodeHandler extends StorageHandler {
@Autowired
GroupProductMapper groupProductMapper;
@Autowired
GroupProductDetailMapper groupProductDetailMapper;
@Autowired
ProductManageControllerFeign productManageControllerFeign;
@Override
public List<OrderDetailDO> doStorage(
OrderDO orderDO,
List<OrderDetailDO> detailList,
StoreDO storeDO,
ProcessOrderHandlerDto.ProcessOrderDetailDto orderDetailDto
) {
//查组合编码
GroupProductDO groupProductDO = groupProductMapper.selectOne(
new LambdaQueryWrapper<GroupProductDO>()
.eq(GroupProductDO::getStoreId, storeDO.getId())
.eq(GroupProductDO::getGroupProductNo, orderDetailDto.getProductBarCode())
);
if (Objects.nonNull(groupProductDO)) {
List<GroupProductDetailDO> groupProductDetailList = groupProductDetailMapper.selectList(
new LambdaQueryWrapper<GroupProductDetailDO>()
.eq(GroupProductDetailDO::getGroupProductId, groupProductDO.getId())
);
for (GroupProductDetailDO groupProductDetailDO : groupProductDetailList) {
OrderProductInfoRpcRequest rpcGroupProductRequest = new OrderProductInfoRpcRequest();
rpcGroupProductRequest.setCargoOwnerId(storeDO.getCargoOwnerId());
rpcGroupProductRequest.setWarehouseId(orderDO.getWarehouseId());
///查系统中商品账册信息
JsonData<OrderProductInfoVo> groupProductInfoJsonData = productManageControllerFeign
.getOrderProductInfoRPC(rpcGroupProductRequest);
if (JsonData.isSuccess(groupProductInfoJsonData)) {
log.warn("订单:{}, 匹配到组合编码落库规则,并查到账册信息",
orderDO.getOutTradeNo()
);
OrderProductInfoVo groupProductInfoVo = groupProductInfoJsonData.getData();
OrderDetailDO detailDO = new OrderDetailDO();
//商品id
detailDO.setProductId(groupProductInfoVo.getProductId());
//商品名称
detailDO.setProductName(groupProductInfoVo.getProductName());
......
detailList.add(detailDO);
}
}
return detailList;
}
StorageHandler next = super.next();
//当前为最后一个规则,即没匹配到任何规则,给默认值
if (null == next) {
log.warn("订单:{}, 当前规则:{} 没匹配任何落库规则,给默认值!!!",
orderDO.getOutTradeNo(),
"组合编码"
);
OrderDetailDO detailDO = super.defaultInfo(orderDO, orderDetailDto, null, null, null);
detailList.add(detailDO);
return detailList;
}
return next.doStorage(orderDO, detailList, storeDO, orderDetailDto);
}
}
分销编码规则
/**
* @description:分销编码落库规则
*/
@Slf4j
@Component
public class DistributionCodeHandler extends StorageHandler {
@Autowired
private ProductDistributionPoolMapper productDistributionPoolMapper;
@Override
public List<OrderDetailDO> doStorage(
OrderDO orderDO,
List<OrderDetailDO> detailList,
StoreDO storeDO,
ProcessOrderHandlerDto.ProcessOrderDetailDto orderDetailDto
) {
ProductDistributionPoolDO productDistributionPoolDO = productDistributionPoolMapper.selectOne(
new LambdaQueryWrapper<ProductDistributionPoolDO>()
.eq(ProductDistributionPoolDO::getDistributionCode, orderDetailDto.getProductBarCode())
);
if (Objects.nonNull(productDistributionPoolDO)) {
log.warn("订单:{}, 匹配到分销编码落库规则",
orderDO.getOutTradeNo()
);
//落库系统中备案的信息
OrderDetailDO detailDO = new OrderDetailDO();
//商品id
detailDO.setProductId(productDistributionPoolDO.getProductId());
//商品名称
detailDO.setProductName(productDistributionPoolDO.getProductName());
......
detailList.add(detailDO);
return detailList;
}
StorageHandler next = super.next();
//当前为最后一个规则,即没匹配到任何规则,给默认值
if (null == next) {
log.warn("订单:{}, 当前规则:{} 没匹配任何落库规则,给默认值!!!",
orderDO.getOutTradeNo(),
"分销编码"
);
OrderDetailDO detailDO = super.defaultInfo(orderDO, orderDetailDto, null, null, null);
detailList.add(detailDO);
return detailList;
}
return next.doStorage(orderDO, detailList, storeDO, orderDetailDto);
}
}
调用代码
//遍历拉单的信息
for (ProcessOrderHandlerDto.ProcessOrderDetailDto orderDetailDto : dto.getDetailList()) {
List<OrderDetailDO> tmpList = new ArrayList<>();
//根据规则落库
StorageHandler storageHandler = productIDHandler
.appendHandler(groupCodeHandler.appendHandler(distributionCodeHandler));
tmpList = storageHandler.doStorage(orderDO, tmpList, storeDO, orderDetailDto);
detailList.addAll(tmpList);
}