为了优化代码中对于不同类型或者状态来进行不同的业务处理的if判断代码,使用设计模式进行解决。
背景交代:
- 某个业务的数据有若干种类型,每一个类型都会对应不同的业务逻辑,会调用不同的库、第三方等差有异化的逻辑。如果日后在新增类型,就会在调用方进行修改调整,切每个类型对应的业务处理逻辑不便于查找和管理。
思路:
- 为了将此类业务代码规范化,并且将每个类型的处理逻辑与调用此类业务的代码进行剥离,从而达成再有新的类型加入时,老代码不需要进行修改,新代码只需要传入特定的数据就可以进入不同的业务逻辑的优点,并且将每个实现策略统一化管理,而且可以避免长时间多版本迭代后多重条件(if-else)语句出现的代码逻辑混乱且难维护的问题。
关于本demo的问题:
- 因为没有新建一个spring项目,所以本应该增加的spring注解并没有在代码中体现出来,所以如果要在spring环境中使用,需自行根据需求补齐spring相关注解。
- 代码中的类命名以及变量命名不具备参考性,请自行修改。
关于本模式的问题:
- 调用端必须知道所有的策略类,并自行决定使用哪一个策略类。也就代表调用者必须清楚参数对应的策略有何种区别。
- 由于该模式把每个具体的策略的具体实现都单独封装成为类,备选的策略过多,那么对象的数目就会很可观。
- 开发效率不会比if-else快。(那是肯定的)
代码结构:
- abstractExecutor - InventoryControlAbstractExecutor 为抽象类,内含抽象方法一个。
- enums - ChangeReasonEnum 为需要根据某个业务字段判断的类型枚举。
- factory - ActivityFactory 为工厂以及策略判断
- impl - 若干Executor 为每一种类型的具体实现方法。
- Invoking 模拟调用而已
abstractExecutor - InventoryControlAbstractExecutor
抽象类和抽象方法
/** * 库存计算执行器 */ public abstract class InventoryControlAbstractExecutor { /** * 根据数量变动原因重新计算某些库存数 * @param changeReason 数量变动原因 * @param num 数量 */ public abstract List<Object> inventoryCalculationExecute(Integer changeReason, Integer num); }
- 抽象类和抽象方法描述了此类业务的特性。
- 此方法的入参和返参需要根据业务而定,如果无法确认写一些很抽象的数据类型也是可以的。
- 例如此代码中,即便日后新增新的类型,那么变动原因类型changeReason是此类方法必传的参数,数量num也是此类方法必传的参数。而返回参数也是此类业务共同的返回类型。
- 在使用此种设计模式下,建议开发者优先想清楚你的业务逻辑需要什么样的参数,返回什么样的参数,从而制定抽象方法是什么样的。
enums - ChangeReasonEnum
区分不同业务逻辑的类型枚举
/** * 库存调整类型枚举 */ public enum ChangeReasonEnum { SOLD_INVENTORY("售出", 1, "soldInventoryExecutor"), ORDER_CANCELLATION("取消", 2, "orderCancellationExecutor"), DRUG_RECOVERY("取货后", 3, "drugRecoveryExecutor"); private String name; private int val; private String engAbbreviation; }
- 为节省无用篇幅,代码中没有写对应的构造方法、get set方法,请自行补充。
- 该枚举中需要表明出一共有多少种业务类型,并且每一种业务类型对应的执行器名称。
- 此处的执行器名称需要对应本模式中 impl 下 若干Executor的文件名,并首字母小写。例如,其中一中业务逻辑的实现类为 DrugRecoveryExecutor.java 那么,此处枚举的 engAbbreviation 字段就需要填写 drugRecoveryExecutor。
- 当然,并不是只能用 engAbbreviation 字段来记录该名称,此处逻辑在ActivityFactory代码中的static静态块中的enums.getEngAbbreviation() 处更改取值字段即可。
- 假设调用方传入3, 那么就会去查找名为drugRecoveryExecutor的Bean,于是自然就会根据策略将业务逻辑指向DrugRecoveryExecutor.java中所编写的。
factory - ActivityFactory
工厂以及策略判断
// @Service public class ActivityFactory { // 存放所有库存调整的类型 private static final Map<Integer, String> inventoryTypes = new ConcurrentHashMap<>(); // 优先获取没枚举类中配置的所有的库存类型 static { ChangeReasonEnum[] InventoryChangeReasonEnums = ChangeReasonEnum.values(); for (ChangeReasonEnum enums : InventoryChangeReasonEnums) { inventoryTypes.put(enums.getVal(), enums.getEngAbbreviation()); } } // 通过Map注入,通过 spring bean 的名称作为key动态获取对应实例 @Resource private Map<String, InventoryControlAbstractExecutor> executorMap; /** * 工厂层执行器 * @param changeReason 库存变动原因 * @param num 数量 */ public List<Object> execute(Integer changeReason, Integer num) { String inventoryType = inventoryTypes.get(changeReason); if (inventoryType == null || inventoryType.isEmpty()) { return null; } // 决定最终走哪个类的执行器 InventoryControlAbstractExecutor executor = executorMap.get(inventoryType); if (executor == null) { return null; } return executor.inventoryCalculationExecute(changeReason, num); } }
- inventoryTypes的map中存放了枚举中的值以及对应的bean名。
- executorMap的map中存放了spring启动后把各个逻辑的实现类托管到容器中,此map中存放了继承抽象类的所有业务具体实现。
- execute方法根据调用者传入的参数,来从inventoryTypes获取枚举信息,从而找到对应的Bean,然后把参数交给相应的执行器进行处理。
impl - 若干Executor 为每一种类型的具体实现方法
/** * 取货后 */ // @Service public class DrugRecoveryExecutor extends InventoryControlAbstractExecutor { /** * 取货后执行器 * 取货后状态的数量变更逻辑在此编写 */ @Override public List<Object> inventoryCalculationExecute(Integer changeReason, Integer num) { // 查询库存信息 // 根据数量重新计算库存信息 return new ArrayList<>(); } }
/** * 取消 */ // @Service public class OrderCancellationExecutor extends InventoryControlAbstractExecutor { /** * 取消执行器 * 取货后状态的数量变更逻辑在此编写 */ @Override public List<Object> inventoryCalculationExecute(Integer changeReason, Integer num) { // 查询库存信息 // 根据数量重新计算库存信息 return new ArrayList<>(); } }
- impl包中的每一个类都是一种类型对应的具体业务逻辑,每一个类都需要继承abstractExecutor包中的抽象类。 本demo中就需要继InventoryControlAbstractExecutor
- 在Override的方法中填入此类型所对应的业务逻辑即可
Invoking 模拟调用
// 图省事,假装有个@Service public class Invoking { @Resource private ActivityFactory activityFactory; // 此处方法模拟业务方法, public void serviceImplFunction(String[] args) { // 传入不同的参数获取不同参数所对应的结果值 List<Object> execute = activityFactory.execute(2, 150); } }
- 模拟调用
- 拿到工厂bean后调用execute方法,填入不同的入参就可以自动根据填写的参数来进行不同的业务逻辑处理并拿到返回数据。