【策略模式在项目中的实际应用】

业务场景

最最近项目中有这样的一个业务场景:

用户下单->管理员审核->配送员接单->配送中->送达-->签收->完成

整个业务以这种流程的形式存在,每个流程状态的业务不一样,考虑到多种状态如果直接写一个接口肯定会嵌套太多的if else,于是这里使用了策略模式。

对策略模式的基础这里不做概述

接口设计

策略行为接口的设计

java 复制代码
public interface OrderTraceChangeStrategy {
    /**
     * 用于判断策略是否支持
     *
     * @param traceDto
     * @param wxPreOrder
     * @return
     */
    boolean support(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder);

    /**
     * 业务参数校验
     *
     * @param traceDto
     * @param wxPreOrder
     * @return
     */
    void check(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder);

    /**
     * 状态变操作
     *
     * @param traceDto
     * @param wxPreOrder
     */
    void change(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder);
}

定义了一个接口,该接口提供了三个方法:support、check、change。

support方法用于判断策略是否支持

check方法用于对业务参数进行校验

change方法用于执行状态转换以及其他业务操作操作

策略实现类的设计

这里列举一个实现类

java 复制代码
@Slf4j
@Component
public class AuditRefuseStrategyImpl implements OrderTraceChangeStrategy {
 @Resource
 private WxPreOrderService wxPreOrderService;

 @Override
 public boolean support(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder) {
     return traceDto.getAfterPreOrderStatusEnum()==PreOrderStatusEnum.REJECT_REQ;
 }

 @Override
 public void check(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder) {
     //当前订单为待审核才可以拒单
     Assert.isTrue(wxPreOrder.getOrderStatus().equals(PreOrderStatusEnum.WAIT_CHECK.getCode()), WeiXinError.COMMON_ERROR, "当前订单状态非待审核状态,不支持拒单");
 }

 @Override
 @Transactional(rollbackFor = Throwable.class)
 public void change(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder) {
     //更新主表数据
     WxPreOrder wxPreOrderUpdate = new WxPreOrder();
     wxPreOrderUpdate.setId(wxPreOrder.getId());
     //变更为拒单状态
     wxPreOrderUpdate.setOrderStatus(PreOrderStatusEnum.REJECT_REQ.getCode());
     wxPreOrderUpdate.setRejectReqReason(traceDto.getRemark());
     wxPreOrderService.updateById(wxPreOrderUpdate);
     log.info("订单状态流转,订单id:{},审核拒绝:{}------------>{}", wxPreOrder.getId(), wxPreOrder.getOrderStatus(), traceDto.getAfterPreOrderStatusEnum().getCode());
 }
}

业务层的应用

java 复制代码
@Autowired
private List<OrderTraceChangeStrategy> orderTraceChangeStrategy;


@Override
   @Transactional(rollbackFor = Throwable.class)
   public void traceOrderStatus(WxPreOrderTraceDto traceDto) {
       //查询当前订单信息
       WxPreOrder preOrder = wxPreOrderService.getById(traceDto.getOrderId());
       Assert.isNotNull(preOrder, WeiXinError.COMMON_ERROR, "预订单不存在");
       for (OrderTraceChangeStrategy item : orderTraceChangeStrategy) {
           //获取支持的策略
           boolean support = item.support(traceDto, preOrder);
           if (support) {
               //参数校验
               item.check(traceDto, preOrder);
               //业务处理
               item.change(traceDto, preOrder);
               //公共业务保存订单状态变更流水信息
               WxPreOrderSaveFlowDto saveFlowDto = WxPreOrderSaveFlowDto.builder().preStatus(preOrder.getOrderStatus()).afterStatus(traceDto.getAfterPreOrderStatusEnum().getCode()).orderId(preOrder.getId()).build();
               saveOrderFlow(saveFlowDto);
               //处理完业务直接返回
               return;
           }
       }
       //上面已经return了 如果走到这里说明前端传递的参数没有和任何策略命中,直接抛出异常
       Assert.Error( WeiXinError.COMMON_ERROR, "错误的订单状态");
   }

重点来了:这里使用一次注入多个策略的方式,直接注入到容器一个集合,对于这些集合中的策略执行哪一个,使用

循环判断的方式。

接受参数使用枚举的方式,使用枚举对应类型强校验,不传参会相应前端页面400。

关于枚举在项目中的使用骚操作,后面的文章还会更新。请持续关注,保证你眼前一亮。

策略业务对应的入参:

java 复制代码
Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxPreOrderTraceDto {

   @ApiModelProperty("订单ID")
   @NotNull(message = "订单ID不能为空")
   private Long orderId;
   /**
    * 使用枚举强校验,前端传错会报400
    */
   @ApiModelProperty("订单变更后的状态")
   @NotNull(message = "流程变更状态不能为空")
   private PreOrderStatusEnum afterPreOrderStatusEnum;
   @ApiModelProperty("原因,备注")
   private String remark;

这样就实现了不同流程状态的业务拆分,即使再多的状态也不用担心代码无法维护了。

其实还有一个需要解释一下,在业务层,下面这些代码,其实属于设计模式中的模板方法,将这些业务统一抽取成一个

模版,执行类似的业务,也可以在抽象类中去继续封装这些算法行为。这里暂时不做赘述。

java 复制代码
                //参数校验
                item.check(traceDto, preOrder);
                //业务处理
                item.change(traceDto, preOrder);
                //公共业务保存订单状态变更流水信息
                WxPreOrderSaveFlowDto saveFlowDto = WxPreOrderSaveFlowDto.builder().preStatus(preOrder.getOrderStatus()).afterStatus(traceDto.getAfterPreOrderStatusEnum().getCode()).orderId(preOrder.getId()).build();
                saveOrderFlow(saveFlowDto);
                //处理完业务直接返回
      

总结一下

  1. 定义策略接口,定义不同行为
  2. 通过List批量注入不同策略
  3. 通过不同状态枚举判断不同状态的业务,获取不同状态的策略类
  4. 通过模板方法,抽取公共业务
相关推荐
晚夜微雨问海棠呀4 分钟前
长沙景区数据分析项目实现
开发语言·python·信息可视化
graceyun5 分钟前
C语言初阶习题【9】数9的个数
c语言·开发语言
波音彬要多做39 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
Swift社区1 小时前
Excel 列名称转换问题 Swift 解答
开发语言·excel·swift
一道微光1 小时前
Mac的M2芯片运行lightgbm报错,其他python包可用,x86_x64架构运行
开发语言·python·macos
矛取矛求1 小时前
QT的前景与互联网岗位发展
开发语言·qt
Leventure_轩先生1 小时前
[WASAPI]从Qt MultipleMedia来看WASAPI
开发语言·qt
向宇it1 小时前
【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity
开发语言·unity·c#·游戏引擎
是娜个二叉树!1 小时前
图像处理基础 | 格式转换.rgb转.jpg 灰度图 python
开发语言·python
Schwertlilien1 小时前
图像处理-Ch5-图像复原与重建
c语言·开发语言·机器学习