zoukankan      html  css  js  c++  java
  • 如何在Spring Boot项目中巧妙利用策略模式干掉if else!

    直入主题

    我们都知道,设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
    那么,我们可能都了解过设计模式,但是在项目中怎么使用可能还是会有点疑惑,今天,公司的项目刚好有一个场景来让我使用一个设计模式:策略模式。

    场景

    关于用户订单充值(订单支付同理),我们都知道,现今的支付方式是非常的多的,例如:支付宝、微信、银联、钱包(各个APP的账户余额)等等。
    查询实体Query:

    /**
     * @author Howinfun
     * @desc 查询
     * @date 2019/10/22
     */
    @Data
    public class ChargeQuery {
        
        /** 支付方式(ALI/WX/UNION) */
        @NotBlank(message = "支付方式不能为空",groups = PayWayNotBlank.class)
        private String payWay;
        
        /** 充值金额 */
        @NotNull(message = "充值金额不能为空",groups = AmountNotNull.class)
        private Double amount;
    }
    

    Service接口:

    /**
     * @author Howinfun
     * @desc 充电-充值模块
     * @date 2019/10/30
     */
    public interface ChargeRechargeService {
    
        /**
         * 根据不用支付方式进行用户余额充值
         * @param query
         * @return
         */
        Result recharge(ChargeQuery query);
    
        /**
         * 充值回调
         * @param rechargeCallBack
         */
        Result rechargeCallBack(RechargeCallBack rechargeCallBack);
    }
    

    传统方式实现

    就是利用if else或者switch来进行条件判断:

    /**
     * @author Howinfun
     * @desc
     * @date 2019/10/30
     */
    @Service
    @AllArgsConstructor
    @Slf4j
    public class ChargeRechargeServiceImpl implements ChargeRechargeService {
    
        private final CarUserMapper carUserMapper;
        private final IncomePaymentMapper incomePaymentMapper;
        private final RechargeRecordMapper rechargeRecordMapper;
        private final PayWayHandlerContext payWayHandlerContext;
    
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Result recharge(ChargeQuery query) {
            Result result = new Result();
            // ......
            // ......
            if (PayConstant.PAY_WAY_WX.equals(query.getPayWay())){
                // 微信
                // ......
            }else if (PayConstant.PAY_WAY_ALI.equals(query.getPayWay())){
                // 支付宝
                // ......
    
            }else if (PayConstant.PAY_WAY_UNION_PAY.equals(query.getPayWay())){
                 // 银联
                 // ......
        
            }
            return result;
        }
    }
    

    总结:我们可以看到,传统的实现方式是非常的笨重的,而且代码非常的不简洁,扩展性差。假如我们要接入新的支付方式,那么我们只能继续添加 else if。

    策略模式

    Talk is cheap,show me the code.
    我们先看一下,如果使用策略模式,service的代码将变成啥样。

    /**
     * @author Howinfun
     * @desc
     * @date 2019/10/30
     */
    @Service
    @AllArgsConstructor
    @Slf4j
    public class ChargeRechargeServiceImpl implements ChargeRechargeService {
    
        private final PayWayHandlerContext payWayHandlerContext;
    
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Result recharge(ChargeQuery query) {
            return this.payWayHandlerContext.getHandlerInstance(query.getPayWay()).handler(query);
        }
    }
    

    emmmm,确实是简单了不少。不但代码量少了,简洁了,而且不再担心因为新增支付方式而修改serviceImpl的代码了。

    下面进行详细的讲解:
    1、首先,我们需要自定义一个注解,来标识一个支付类型对应的一个处理器。

    /**
     * @author Howinfun
     * @desc 自定义注解,标识支付类型
     * @date 2019/11/2
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface PayWayHandler {
    
        String value();
    }
    

    2、接着,抽象一个处理器,让每个支付方式的处理器继承此抽象处理器,实现handle方法:

    /**
     * @author Howinfun
     * @desc 抽象订单类型处理器
     * @date 2019/11/2
     */
    public abstract class AbstractPayWayHandler {
    
        /**
         *
         * @param query
         * @return
         */
        abstract public Result handler(ChargeQuery query);
    }
    

    3、实现类,例如支付宝、微信、银联:
    注意:每个处理器都要加上@component,交给Spring管理。

    /**
     * @author Howinfun
     * @desc 支付宝支付
     * @date 2019/11/2
     */
    @Component
    @PayWayHandler("ALI")
    @Slf4j
    @AllArgsConstructor
    public class AliPayWayHandler extends AbstractPayWayHandler {
        
        // ....各种依赖
    
        @Override
        public Result handler(ChargeQuery query) {
            Result result = new Result();
            // ......
            return result;
        }
    }
    
    /**
     * @author Howinfun
     * @desc 微信支付
     * @date 2019/11/2
     */
    @Component
    @PayWayHandler("WX")
    @Slf4j
    @AllArgsConstructor
    public class WxPayWayHandler extends AbstractPayWayHandler {
        
        // ....各种依赖
    
        @Override
        public Result handler(ChargeQuery query) {
            Result result = new Result();
            // ......
            return result;
        }
    }
    
    /**
     * @author Howinfun
     * @desc 银联支付
     * @date 2019/11/2
     */
    @Component
    @PayWayHandler("UNION")
    @Slf4j
    @AllArgsConstructor
    public class UnionPayWayHandler extends AbstractPayWayHandler {
        
        // ....各种依赖
    
        @Override
        public Result handler(ChargeQuery query) {
            Result result = new Result();
            // ......
            return result;
        }
    }
    

    4、然后最重点的来了,创建一个类,实现ApplicationContextAware接口,重写setApplicationContext方法,然后扫描带有自定义注解@PayWayHandler的Bean,然后存储起来,方便Service的获取。

    /**
     * @author Howinfun
     * @desc
     * @date 2019/11/2
     */
    @Component
    public class PayWayHandlerContext implements ApplicationContextAware {
    
        @Autowired ApplicationContext applicationContext;
    
        /** key为PayWay,value为class*/
        private static final Map<String,Class> handlerMap = new HashMap<>(10);
    
        public AbstractPayWayHandler getHandlerInstance(String payType){
            Class clazz = handlerMap.get(payType);
            if (clazz == null){
                throw new CustomDeniedException("暂不支持此支付方式");
            }
            return (AbstractPayWayHandler) applicationContext.getBean(clazz);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            // 遍历带有PayTypeHandler注释的类
            Map<String,Object> beans = applicationContext.getBeansWithAnnotation(PayWayHandler.class);
            if (beans != null && beans.size() > 0) {
                for (Object serviceBean : beans.values()) {
                    String payType = serviceBean.getClass().getAnnotation(PayWayHandler.class).value();
                    handlerMap.put(payType, serviceBean.getClass());
                }
            }
        }
    }
    

    总结:到此,ServiceImpl可根据前端传过来的payWay来选择对应的handler来处理。我利用了策略模式简化了繁杂的 if else 代码,并且扩展性得到了大大的提升,不再担心因为支付方式的新增而修改业务代码。

  • 相关阅读:
    你不知道的空格
    导致你的微服务走向失败的11个原因
    阿里巴巴正式开源 Inclavare Containers 技术
    如何分辨区块链传销项目?
    编写干净的 React Components & JSX
    面试:3年工作经验程序员应有的技能
    数据库设计规范化的 5 个要求
    mac下镜像飞速安装Homebrew教程
    对优秀程序员的思考
    程序员理想中的工作环境
  • 原文地址:https://www.cnblogs.com/Howinfun/p/11785460.html
Copyright © 2011-2022 走看看