代码重构思路(下单流程)

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

举个栗子

我任职于一家小电商,我这边的下单接口有如下的业务流程:

光看这流程图,就可以感知复杂。我在重构时就利用了设计模式,加强了它的扩展性、可读性。

模版方法

例如优惠券校验这个一小部分,伪代码如下:

if 是否使用优惠券 then 
优惠券校验
else if 
//do nothing
endif 

我第一个想到的就是模版方法。

将不变的部分放在父类,由不同的子类去实现变化的部分。

这里变的部分是校验逻辑,不变的是 if-else。

父类 AbstractOrderSubmitTransactionTemplate 如下:

public abstract class AbstractOrderSubmitTransactionTemplate implements OrderSubmitTransactionTemplate{
		protected abstract boolean test(Context context);
        protected abstract void doValidate(Context context);
        protected abstract void doPrepare(Context context);
        protected abstract void doCommit(Context context);
        protected abstract void doRollback(Context context);

        @Override
        public void validate(Context context) {
            if(test(context)){
                //日志记录
                doValidate(context);
                //日志记录
            }
        }

        @Override
        public final void prepare(Context context) {
            if(test(context)){
                //日志记录
                doPrepare(context);
                //日志记录
            }
        }

        @Override
        public final void commit(Context context) {
            if(test(context)){
                //日志记录
                doCommit(context);
                //日志记录
            }
        }

        @Override
        public final void rollback(Context context) {
            if(test(context)){
                //日志记录
                doRollback(context);
                //日志记录
            }
        }
    }

根据接口分离原则,定义供调用者调用的接口 OrderSubmitTransactionTemplate

 public interface  OrderSubmitTransactionTemplate{
		 void validate(Context context);
         void prepare(Context context);
         void commit(Context context);
         void rollback(Context context);
}

举例,用于处理优惠券的子类 CouponTransactionTemplate 实现:

public class CouponTransactionTemplate extends AbstractOrderSubmitTransactionTemplate{

        @Override
        public boolean test(Context context) {
            return context.isUseCoupon();
        }

        @Override
        protected void doValidate(Context context) {
            //优惠券校验
        }

        @Override
        protected void doPrepare(Context context) {
            //优惠券预扣减
        }

        @Override
        protected void doCommit(Context context) {
            //优惠券扣减提交
        }

        @Override
        protected void doRollback(Context context) {
            //优惠券扣减回滚
        }
    }

基于模版方法的改造就完成了,我的业务代码也就变成了下面这样:

public void submit(Context context){
            //校验
            stockTransactionTemplate.validate(context);
            couponTransactionTemplate.validate(context);
            activityTransactionTemplate.validate(context);
            //预扣减
            stockTransactionTemplate.prepare(context);
            couponTransactionTemplate.prepare(context);
            activityTransactionTemplate.prepare(context);
            try{
                //下单流程
                //确认扣减
                stockTransactionTemplate.commit(context);
                couponTransactionTemplate.commit(context);
                activityTransactionTemplate.commit(context);
            }catch (Exception e){
                //回滚扣减
                stockTransactionTemplate.rollback(context);
                couponTransactionTemplate.rollback(context);
                activityTransactionTemplate.rollback(context);
            }
}

责任链模式

光使用模版方法改造,其实还不够,只是减少了 if-else,并没有符合开闭原则,也就是说当我需要加入一种全新的优惠券时,又需要改动我下单方法(submit

所以,我又想到了责任链模式。

调用者将请求传给责任链,具体哪些个对象负责执行,由责任链上的对象自行判断是否处理。

我在 AbstractOrderSubmitTransactionTemplate 类中定义的 test 方法,其实已经可以实现对象自行判断是否处理。所以这里我只要把所有的 AbstractOrderSubmitTransactionTemplate 子类挨个轮询就可以实现责任链模式。

例如校验方法(validate)可以这样实现:

@Autowired
private List<AbstractOrderSubmitTransactionTemplate> transactionTemplates;
public void validate(Context context){
            for (AbstractOrderSubmitTransactionTemplate transactionTemplate : transactionTemplates) {
                transactionTemplate.validate(context);
            }
        }

这里可以利用 spring 的 @Autowired 注解,将 AbstractOrderSubmitTransactionTemplate 的子类注入到一个 list 中。

经过改造后,我的业务代码成了这样:

		public void submit(Context context){
            //校验
            validate(context);
            //预扣减
            prepare(context);
            try{
                //下单流程
                //确认扣减
                commit(context);
            }catch (Exception e){
                //回滚扣减
                rollback(context);
            }
        }

策略模式

假设某一天,随着公司业务扩张,需要根据不同的订单类型,执行不同的下单逻辑。也就是我的下单流程要改,有些订单类型插入 A 库,有些订单类型插入 B 库,有些订单类型可能不插库直接放到其他系统去。

伪代码如下

//校验
//预扣减
//下单流程
if 订单类型为1 then
插入A库
else if 订单类型为2 then
插入B库
else if 订单类型为3 then
调用C系统同步订单
end if
//扣减提交/扣减回滚

校验、预扣减、扣减提交 / 扣减回滚这块和下单流程这块是一对多的关系,所以我这边想到的是利用策略模式去实现这个需求。

业务模块之间是 1 对多的关系,将每个模块封装成策略,组合使用。

下单流程策略接口 OrderSubmitStrategy:

    public  interface OrderSubmitStrategy{
        void submit(Context context);
    }


依旧使用模版方法定义了抽象的父类

public  abstract class AbstractOrderSubmitStrategy implements OrderSubmitStrategy {
        protected abstract  void doSubmit(Context context);
        public final void submit(Context context){
            //日志记录
            this.doSubmit(context);
            //日志记录
        }
    }

其中一个子类如下:

public class OrderSubmitV1Strategy extends AbstractOrderSubmitStrategy{

        @Override
        protected void doSubmit(Context context) {
            //v1 下单
        }
    }

经过改造,我的业务代码变成了这样:

       public void submit(Context context,OrderSubmitStrategy orderSubmitStrategy){
            //校验
            validate(context);
            //预扣减
            prepare(context);
            try{
                //下单流程
                orderSubmitStrategy.submit(context);
                //确认扣减
                commit(context);
            }catch (Exception e){
                //回滚扣减
                rollback(context);
            }
        }


在具体调用时,根据不同策略执行不同的下单逻辑:

@PostMapping("/v1/submit")
    public SubmitRes submit(SubmitForm form){
        Context context = new Context(form);
        orderSubmitBean.submit(context,orderSubmitV1Strategy);
        return context.getSubmitRes();
    }

    @PostMapping("/v2/submit")
    public SubmitRes submit(SubmitForm form){
        Context context = new Context(form);
        orderSubmitBean.submit(context,orderSubmitV2Strategy);
        return context.getSubmitRes();
    }

观察者模式

下单之后,需要通知其他系统,比如大数据系统。这些通知并不需要保证强一致性,只需要最终一致性,一般我们都利用消息中间件来通知,但是通知多了,发送消息代码的严重影响代码简洁,而且每次有新的消息需要发送都需要改动 submit 方法。

于是,我就想到的观察者模式来实现。

当一个对象被修改时,则会自动通知依赖它的对象

观察者接口:

 public static interface OrderSubmitListener{
        void handle(Context context);
 }

notifyListeners 方法:

@Autowired
private List<OrderSubmitListener> listeners;

        void notifyListeners(Context context){
            for (OrderSubmitListener listener : listeners) {
                listener.handle(context);
            }

        }

这里可以利用 spring 的 @Autowired 注解,将 OrderSubmitListener 的子类注入到一个 list 中。

改造后代码如下:

public void submit(Context context,OrderSubmitStrategy orderSubmitStrategy){
            //校验
            validate(context);
            //预扣减
            prepare(context);
            try{
                //下单流程
                orderSubmitStrategy.submit(context);
                //确认扣减
                commit(context);
            }catch (Exception e){
                //回滚扣减
                rollback(context);
            }
            notifyListeners(context);
        }


posted @ 2022-12-29 10:37  托马斯布莱克  阅读(134)  评论(0编辑  收藏  举报