• Spring boot 运用策略模式实现,避免多次使用if


    前言

    这里就不详细去介绍策略模式是怎么样的了,想了解的可以点击下面的链接

    策略模式介绍的链接:策略模式的介绍

    这里列出策略模式的好处

    场景:某网页有个支付,其中包含了微信、支付宝等方式的支付方式 ,后续明确还会进行兼容其他的支付方式

    用策略模式的好处:

    • 避免多次使用if判断是那种策略进行操作。
    • 因为每种策略(微信支付、支付宝支付)的内容都比较复杂。策略模式能将每种策略分离出来,方面后续维护管理

    下面我们将使用Spring boot 运用策略模式,实现上面的需求

    环境配置

    • JDK8
    • Spring boot 2.3.7.RELEASE
    • 整合了spring-boot-starter-web

    实现目标

    使用策略模式后,新加一种支付策略时,只需要在策略枚举中添加新加的策略信息,外加一个策略类即可,而不再需要添加新的if判断

    准备策略接口和具体实现策略类

    支付策略接口

    /**
     * 支付策略
     */
    public interface PayStrategy {
    
        /**
         * 支付(参数就没具体写了,可以定义成每个支付必须要有的参数)
         * @return
         */
         boolean pay();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    微信支付策略类

    /**
     * 第三方——微信支付(这里注意我修改了Bean的默认命名)
    
     
     */
    @Component("wechatPayStrategy")
    public class WeChatPayStrategyImpl implements PayStrategy{
    
        /**
         * 支付
         * @return
         */
        @Override
        public boolean pay() {
    
            //进行微信的支付逻辑
            System.out.println("正在进行微信的支付逻辑");
    
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    支付宝支付策略类

    
    /**
     * 支付宝第三方支付(这里注意我修改了Bean的默认命名)
     */
    @Component("alipayStrategy")
    public class AliPayStrategyImpl implements PayStrategy {
    
        /**
         * 支付宝支付
         * @return
         */
        @Override
        public boolean pay() {
    
            //进行支付宝支付逻辑
            System.out.println("进行支付宝支付逻辑");
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    上述已将各自的策略的处理类进行了分离,接下来时使用支付策略工厂类和支付策略上下文将各自的策略类联系起来

    准备支付策略上下文Context和支付策略工厂类

    支付策略工厂类

    package com.example.springboot_strategy.strategy.factory;
    
    import com.example.springboot_strategy.enums.PayStrategyEnum;
    import com.example.springboot_strategy.strategy.PayStrategy;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import javax.swing.plaf.synth.SynthTextAreaUI;
    import java.util.Map;
    
    /**
     * 支付策略工厂类
     */
    @Component
    public class PayStrategyFactory {
    
        /**
         * 通过Spring容器的方式注入
         */
        @Resource
        private Map payStrategyMap;
    
        /**
         * 获取对应支付策略类
         * @param payStrategyEnum 支付策略枚举
         */
        public PayStrategy getPayStrategy(PayStrategyEnum payStrategyEnum){
    
            if(!payStrategyMap.containsKey(payStrategyEnum.getClassName())){
                System.out.println("没有对应的支付策略,无法进行支付");
                return null;
            }
    
            return payStrategyMap.get(payStrategyEnum.getClassName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    这里工厂类的逻辑是利用了Spring容器的处理方式,如果有多种类同时实现了某个接口,那么可以使用Map集合接收,Map对应的泛型,String是Bean名称,PayStrategy是每个具体实现类,这样我们就可以使用Bean类型去指定具体的策略类了,然后建立一个支付策略枚举去管理这些Bean名称。同时,也可以将Bean名称与客户端定义的类型进行关联。

    支付策略枚举类

    /**
     * 支付策略类型
     */
    public enum PayStrategyEnum {
        WECHAT_PAY("wechat","wechatPayStrategy","微信支付"),
        ALIPAY("alipay","alipayStrategy","支付宝支付")
        ;
    
        /**
         * 支付策略code
         */
        private String code;
    
        /**
         * bean名称
         */
        private String className;
    
        /**
         * 信息
         */
        private String info;
    
        PayStrategyEnum(String code,String className,String info){
            this.code=code;
            this.className=className;
            this.info=info;
        }
    
    
        public String getCode() {
            return code;
        }
    
        public String getClassName() {
            return className;
        }
    
        public String getInfo() {
            return info;
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    上面枚举类中code代表的是客户端定义的类型(例如我从前端接收到的支付type,这个type可以是这个code),className顾名思义,指的是每种策略的bean名称,info是代表每种策略的内容

    支付策略上下文

    /**
     * 支付策略上下文
     */
    @Component
    public class PayStrategyContext {
        
        @Autowired
        private PayStrategyFactory payStrategyFactory;
    
        /**
         * 支付执行
         * @param payDTO 支付参数
         * @return
         */
        public boolean payHandle(PayDTO payDTO){
           
            //将某属性的值转换成具体的枚举。这里是根据payDTO的type字段对应枚举的code进行转换
            Optional payStrategyEnumOptional = Arrays.stream(PayStrategyEnum.class.getEnumConstants())
                    .filter((e) -> e.getCode().equals(payDTO.getType())).findAny();
    
            if(!payStrategyEnumOptional.isPresent()){
                System.out.println("匹配不到具体支付策略");
                return false;
            }
            PayStrategyEnum payStrategyEnum = payStrategyEnumOptional.get();
    
            PayStrategy payStrategy = payStrategyFactory.getPayStrategy(payStrategyEnum);
    
            //进行payDto参数的处理.....
    
            boolean pay = payStrategy.pay();
    
            //支付后的记录处理..
    
    
            return true;
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    pageDto类

    /**
     * 支付DTO
     */
    public class PayDTO {
    
        /**
         * 支付类型
         */
        private String type;
    
    
        /**
         * 支付金额
         */
        private BigDecimal payMoney;
        
        /**
         * ...........
         */
    
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public BigDecimal getPayMoney() {
            return payMoney;
        }
    
        public void setPayMoney(BigDecimal payMoney) {
            this.payMoney = payMoney;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    这个策略上下文,则是选择策略的入口,这里会进行参数的处理,将这里我就将pageDTO类中的type字符串转换成对应的枚举类。
    到这里使用策略模式的编写算是完成了,下面进行编写客户端的代码

    客户端代码

    支付控制器

    @RestController
    @RequestMapping("pay")
    public class PayController {
    
        @Autowired
        private PayStrategyContext payStrategyContext;
    
        @PostMapping
        public boolean pay(@RequestBody PayDTO payDTO){
        
            //这里因为懒。。就没有加上Service层了,直接在控制器处理
            return payStrategyContext.payHandle(payDTO);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    效果

    请添加图片描述

    新需求

    后续新增一个银联的支付方式,我们只需要添加银联的支付策略类和添加银联的支付枚举即可实现

    添加银联的支付策略类

    
    /**
     * 银联支付(这里注意我修改了Bean的默认命名)
     */
    @Component("unionPayStrategy")
    public class UnionPayStrategyImp implements PayStrategy {
    
    
        /**
         * 银联支付
         * @return
         */
        @Override
        public boolean pay() {
    
            //进行银联的支付
    
            System.out.println("进行银联的支付逻辑");
            return true;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在枚举类中添加银联的支付枚举

    /**
     * 支付策略类型
     */
    public enum PayStrategyEnum {
        WECHAT_PAY("wechat","wechatPayStrategy","微信支付"),
        ALIPAY("alipay","alipayStrategy","支付宝支付"),
        UNION_PAY("unionpay","unionPayStrategy","银联支付")
        ;
    
        /**
         * 支付策略code
         */
        private String code;
    
        /**
         * bean名称
         */
        private String className;
    
        /**
         * 信息
         */
        private String info;
    
        PayStrategyEnum(String code,String className,String info){
            this.code=code;
            this.className=className;
            this.info=info;
        }
    
    
        public String getCode() {
            return code;
        }
    
        public String getClassName() {
            return className;
        }
    
        public String getInfo() {
            return info;
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    实现效果

    请添加图片描述

    以上是我使用Spring boot 运用策略模式实现的效果,如果有误人子弟的地方,望在评论区指出。

  • 相关阅读:
    代理配置及多套环境的解决方案
    数字先锋 | 教育资源乘云而来!46万城乡学子共享名师课堂
    Android Jetpack系列(七):Room(使用篇)
    JS 高级
    mpvue进阶
    更新.gitmodules的子模块仓库地址,但是没有生效,需要运行命令
    Echarts散点图筛选新玩法dataZoom
    Docker 容器跨主机通信 - Flannel
    PettingZoo:多智能体游戏环境库入门
    sql题一(空位连续座位买票)
  • 原文地址:https://blog.csdn.net/MDZZ666/article/details/126473264