策略模式实战

type
status
date
slug
summary
tags
category
icon
password
AI 摘要

策略模式实战

实战需求说明

一条单据发布后,需要分别经过PL、DL、SQI三种角色的用户进行处理(不会多个节点同时处理),处理的环节中会将符合条件的数据插入另一个表中。这三个环节的处理顺序是固定的,即发布的单据必须是先PL处理,随后DL再处理,最后SQI处理完成,当前后续可能会新增处理环节,这也就是我想使用设计模式去做的初衷,希望开发出来后后续新增其它环节会比较简单。
其中每个环节处理的逻辑比较复杂,如果简单的使用一个方法去写,则是通过if... else 去判断,当前目前环节不多,也不是不可以,只是这一个方法会非常的长,而且比较难看懂。
处理流程如下:
notion image
需求分配
这种场景的处理流程类似一条流水线,但又不完成是流程线。因为它不会每个环节都处理一次,而是符合某个节点的条件才会处理。
所有感觉策略模式其实也比较符合,但经过对比最终选择使用职责链模式
主要区别:
  1. 职责链模式可以实现逐级委托,每一级的下一级顺序是固定的
  1. 策略模式则路由到不同的处理者上

什么是策略模式?

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
主要构成者:
  1. 策略选择上下文:选择一个策略
  1. 策略接口:策略接口,方便后续扩展
  1. 策略接口实现类:具体的策略实现
  1. 策略抽象类:在这个需求中,因此需要封装一些公共方法,因此增加了一层抽象类
策略模式类图
notion image

原来的实现

java

public ZdmDtHeader completed(ZdmDtHeader zdmDtHeader) { List<ZdmPartsPool> partsPoolList = new ArrayList<>(); List<ZdmDtLine> zdmDtLineAddList = new ArrayList<>(); List<ZdmDtLine> zdmDtLineList = zdmDtHeader.getZdmDtLineList(); //初始化操作记录 QamOperationRecord record = this.initOperationRecord(zdmDtHeader); if (ZdmConstants.DtStatus.PL_ING.equals(zdmDtHeader.getStatus())) { //省略。。。PL状态处理逻辑 } else if (ZdmConstants.DtStatus.DL_ING.equals(zdmDtHeader.getStatus())) { //省略。。。PL状态处理逻辑 } else if (ZdmConstants.DtStatus.SQI_ING.equals(zdmDtHeader.getStatus())) { //省略。。。PL状态处理逻辑 } else { //非处理中状态不可点击操作完成 throw new CommonException(ZdmConstants.ErrorCode.DOCUMENT_STATUS_ERROR); } //省略。。。处理逻辑 return zdmDtHeader; }
Java
原来的实现是在一个方法中,通过if...else去判断,在不同状态下执行不同的处理逻辑。有以下缺点:
  1. 每个状态的处理逻辑较复杂,整个方法非常的长,阅读理解起来比较费劲
  1. 很多if...else语句,后期有可能增加状态处理逻辑,只能继续加if...else
并且业务人员在讲解需求时特意提到,这个需求后续还有变更的可能。。。于是就想着使用一些设计模式。
这个方法的处理逻辑,是根据状态去选择不同的处理逻辑,比较合适使用策略模式。
按照类图,尝试进行策略模式改造。

策略接口

java

/** * @Description 处理者策略接口 * @Date 2022/11/17 09:45 * @Author hjw */ public interface ProcessorStrategy { /** * 获取处理策略的类型 * * @return */ String getStrategyType(); /** * 统一处理方法 * * @param zdmDtHeader */ void process(ZdmDtHeader zdmDtHeader); }
Java
每个策略都需要一个标识,以便我们能进行选择,因此策略接口需要一个获取当前策略类型的方法:x0;getStrategyType() 每个策略类都需要实现这个方法。

策略选择上下文

java

/** * @Description 处理者上下文,用于选择策略 * @Date 2022/11/17 09:42 * @Author hjw */ @Service public class ProcessorStrategyContext { private Map<String,ProcessorStrategy> processorStrategyMap = new HashMap<>(); public ProcessorStrategyContext(List<ProcessorStrategy> processorStrategyList) { //通过构造函数注入策略接口的实现类 for(ProcessorStrategy strategy : processorStrategyList){ processorStrategyMap.put(strategy.getStrategyType(),strategy); } } public void processStrategy(ZdmDtHeader zdmDtHeader){ ProcessorStrategy processor = processorStrategyMap.get(zdmDtHeader.getStatus()); if (processor == null){ //非处理中状态不可点击操作完成 throw new CommonException(ZdmConstants.ErrorCode.DOCUMENT_STATUS_ERROR); } processor.process(zdmDtHeader); } }
Java
  1. 这里通过构造函数注入的方式,将策略的所有实现类注入进来,并按每个策略的类型组成一个Map集合,方便后续选择策略。
  1. ZdmDtHeader 为单据数据,这里我把单据的状态作为选择策略的一个标识。即单据在不同的状态下,点击操作完成按钮时需要去执行不同的处理逻辑(策略)

策略抽象类

java

/** * @Description 处理者抽象类, 封装通用方法 * @Date 2022/11/17 09:45 * @Author hjw */ public abstract class AbstractProcessorService implements ProcessorStrategy { @Autowired protected ZdmDtHeaderRepository zdmDtHeaderRepository; @Autowired protected ZdmDtLineRepository zdmDtLineRepository; // 省略类自动注入 protected QamOperationRecord initOperationRecord(ZdmDtHeader zdmDtHeader) { QamOperationRecord record = new QamOperationRecord(); // 省略初始化逻辑 return record; } protected List<ZdmDtLine> lineSplit(List<ZdmPartsPool> partsPoolListTemp, ZdmDtLine line) { List<ZdmDtLine> zdmDtLineList = new ArrayList<>(); // 省略拆分逻辑 return zdmDtLineList; } protected void initPartsPool(List<ZdmPartsPool> partsPoolList, ZdmDtHeader zdmDtHeader, ZdmDtLine zdmDtLine) { // 省略初始信息 } protected void sendMessage(List<ZdmDtHeader> zdmDtHeaders) { for (ZdmDtHeader zdmDtHeader : zdmDtHeaders) { // 省略消息发送通用方法 } } }
Java
  1. 抽象类实现策略接口,但没有去实现接口中的方法,具体的实现逻辑交给实现类,这里抽象类的主要作用就是封装通用的方法而已
  1. 所有的方法,参数不能使用private类型修饰,否则实现类无法访问

策略实现类

下面是具体的策略实现类,它继承了策略抽象类,可以直接调用抽象类的公共方法

java

/** * @Description PL处理者策略 * @Date 2022/11/17 09:49 * @Author hjw */ @Service public class PlProcessorStrategy extends AbstractProcessorService { @Override public String getStrategyType() { return "PL_ING"; } @Override public void process(ZdmDtHeader zdmDtHeader) { //省略具体处理逻辑 zdmDtHeaderRepository.updateByPrimaryKey(zdmDtHeader); // 实现类中可以直接使用抽象类中注入的类 //省略具体处理逻辑 this.sendMessage(Arrays.asList(zdmDtHeader)); } }
Java
这是第二个策略实现类

java

/** * @Description DL处理者策略 * @Date 2022/11/17 09:49 * @Author hjw */ @Service public class DlProcessorStrategy extends AbstractProcessorService { @Override public String getStrategyType() { return "DL_ING"; } @Override public void process(ZdmDtHeader zdmDtHeader) { //省略具体处理逻辑 } }
Java
  1. 具体的实现类直接继承抽象类即可
  1. 需要实现策略接口中的两个方法,即具体的策略类型与策略处理逻辑
  1. 实现类中可以直接使用抽象类中注入的类与公共方法

客户端调用

下面策略调用方法,也是操作完成按钮的入口方法

java

@Autowired ProcessorStrategyContext processorStrategyContext; @Override public ZdmDtHeader completed(ZdmDtHeader zdmDtHeader) { processorStrategyContext.processStrategy(zdmDtHeader); return zdmDtHeader; }
Java
从之前的几百行,缩短为只一行

整体的类图

整体的类图如下,这里只增加了一层抽象类,其它与标准类图一致
notion image

总结

  1. 策略之间实现解耦,便于修改、扩展
  1. 逻辑更加清晰,每个策略类中只有自己的处理逻辑
  1. 方法更简单,更容易阅读理解
 
上一篇
Feign调用超时时间配置不生效问题排查
下一篇
职责链模式实践
Loading...
2024-11-11
最新发布
ChatGPT与豆包的图像生成
2024-11-12
Windows10家庭版安装Docker记录
2024-11-12
Mybatis Log Parser插件
2024-11-11
职责链模式实践
2024-11-11
Feign调用超时时间配置不生效问题排查
2024-11-11
OpenFeign源码分析
2024-11-11