• 设计模式之策略模式(行为型)


    1、策略模式定义

    代理模式的定义:策略模式是一种行为型模式,定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

    比如商场收银时如何促销,用打折还是返利,其实都是一些算法,这些算法本身就是一种策略,这些算法是随时都可以互相替换的,这就是变化点,而封装变化是我们面向对象的一种很重要的思维方式

    2、策略模式结构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fNJY0iFC-1656173992963)(/var/folders/fj/225wqvz13hq0tflcnkl4y_v40000gn/T/abnerworks.Typora/image-20220625215252869.png)]

    Strategy 类,定义所有支持的算法的公共接口

    /**
     * 抽象策略类
     */
    public abstract class Strategy {
        
        public abstract void  algorithmInterface();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ConcreteStrategy,封装了具体的算法或行为,继承于 Strategy

    /**
     * 具体算法 A
     */
    public class ConcreteStrategyA extends Strategy{
    
        /**
         * 算法 A 实现
         */
        @Override
        public void algorithmInterface() {
            System.out.println("算法 A 实现");
        }
    }
    
    /**
     * 具体算法 B
     */
    public class ConcreteStrategyB extends Strategy {
        
        /**
         * 算法 B 实现
         */
        @Override
        public void algorithmInterface() {
            System.out.println("算法 B 实现");
        }
    }
    
    /**
     * 具体算法 C
     */
    public class ConcreteStrategyC extends Strategy {
    
        /**
         * 算法 C 实现
         */
        @Override
        public void algorithmInterface() {
            System.out.println("算法 C 实现");
        }
    }
    
    • 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

    Context,用一个 ConcreteStrategy 来配置,维护一个对 Strategy 对象的引用

    /**
     * 上下文
     */
    public class Context {
    
        private Strategy strategy;
    
        public Context() {
        }
    
        /**
         * 初始化时传入具体的策略对象
         * @param strategy 具体的策略对象
         */
        public Context(Strategy strategy) {
            this.strategy = strategy;
        }
    
        /**
         * 上下文接口
         */
        public void contextInterface() {
            strategy.algorithmInterface();
        }
    
    }
    
    • 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

    调用方代码

    public class StrategyTest {
    
        /**
         * 因为实例化不同的策略,所以在调用 context.contextInterface(); 时,得到的结果不同
         */
        public static void main(String[] args) {
            Context context;
    
            context = new Context(new ConcreteStrategyA());
            context.contextInterface();
    
            context = new Context(new ConcreteStrategyB());
            context.contextInterface();
    
            context = new Context(new ConcreteStrategyC());
            context.contextInterface();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3、策略模式优缺点

    优点:

    • 由于策略类实现的是同一个抽象,策略类之间可以自由切换
    • 对扩展开放,可以不改动原有代码,增加一个新的策略对策略模式来说比较容易

    缺点:

    • 维护策略类会有额外开销,策略类数量多了的话会比较麻烦
    • 必须对调用方暴露所有的策略类,因为使用哪种策略由调用方决定,调用方需要了解每种策略及它们之间的区别

    4、策略模式的应用场景

    • 几个类的主要逻辑相同,只是部分逻辑的算法和行为上有一些区别的情况
    • 有几种相似的行为,客户端动态的决定使用哪种,我们可以使用策略模式将算法封装起来供客户端使用

    5、策略模式案例实现

    商场收银

    比如商场使用现金收银时,有正常收费 CashNormal、打折收费 CashRebate、和返利收费 CashReturn 三个策略,也就是策略模式中的具体算法,我们使用策略模式来做这样一个设计

    代码结构图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-117HJUM2-1656173992963)(/var/folders/fj/225wqvz13hq0tflcnkl4y_v40000gn/T/abnerworks.Typora/image-20220625225513714.png)]

    现金收费抽象类

    /**
     * 抽象现金策略,收取现金,返回价格
     */
    public abstract class CashStrategy {
    
        /**
         * 接收现金
         * @param money          接收的现金金额
         * @param moneyRebate    打折率
         * @param moneyCondition 返利条件
         * @param moneyReturn    返利值
         * @return 最后的金额
         */
        public abstract BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn);
    
        /**
         * 策略类型
         * @return 策略类型
         */
        public abstract String type();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    正常收费策略

    /**
     * 正常收费子类,原价返回
     */
    @Component
    public class CashNormalStrategy extends CashStrategy {
    
        @Override
        public BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn) {
            return money;
        }
    
        @Override
        public String type() {
            return "normal";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    打折收费策略

    /**
     * 打折收费算法,初始化传入折扣,返回打折后金额
     */
    @Component
    public class CashRebateStrategy extends CashStrategy {
    
        @Override
        public BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn) {
            return money.multiply(BigDecimal.valueOf(moneyRebate > 0 ? moneyRebate : 1.0d)).setScale(2);
        }
    
        @Override
        public String type() {
            return "rebate";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    返利收费策略

    /**
     * 返利收费算法,初始化传入返利条件和返利值
     */
    @Component
    public class CashReturnStrategy extends CashStrategy {
    
        @Override
        public BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn) {
            BigDecimal result = money;
            BigDecimal condition = BigDecimal.valueOf(moneyCondition == null ? 300.0d : moneyCondition);
            BigDecimal returnMoney = BigDecimal.valueOf(moneyReturn == null ? 100.0d : moneyReturn);
            // 如果大于返利条件,要减去返利值
            if (money.compareTo(condition) > -1) {
                result = money.subtract(money.divideAndRemainder(condition)[0].multiply(returnMoney));
            }
            return result.setScale(2);
        }
    
        @Override
        public String type() {
            return "return";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    现金收费决策,根据类型选择策略

    /**
     * 现金策略决策,根据策略类型获取不同策略
     */
    @Component
    public class CashStrategyDecider {
    
        @Resource
        private CashNormalStrategy normalStrategy;
        @Resource
        private CashRebateStrategy rebateStrategy;
        @Resource
        private CashReturnStrategy returnStrategy;
    
        public CashStrategy decideCashStrategy(String strategyType) {
            if (strategyType == null ) {
                return normalStrategy;
            }
            if (strategyType.equals(rebateStrategy.type())) {
                return rebateStrategy;
            } else {
                // todo 扩展点
                if (strategyType.equals(returnStrategy.type())) {
                    return returnStrategy;
                }
            }
            return normalStrategy;
        }
    }
    
    • 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

    调用代码:

    public class CashStrategyTest extends BaseTest {
    
        @Resource
        private CashStrategyDecider cashStrategyDecider;
    
        @Test
        public void cashStrategyTest() {
            CashStrategy cashStrategy;
    
            // 默认算法
            cashStrategy = cashStrategyDecider.decideCashStrategy("");
            BigDecimal defaultResult = cashStrategy.acceptCash(new BigDecimal(200), null, null, null);
            System.out.println("使用默认算法得到的金额为" + defaultResult.doubleValue()); // 使用默认算法得到的金额为200.0
    
            // 打折算法
            cashStrategy = cashStrategyDecider.decideCashStrategy("rebate");
            BigDecimal rebateResult = cashStrategy.acceptCash(new BigDecimal(200), 0.8d, null, null);
            System.out.println("使用打折算法得到的金额为" + rebateResult.doubleValue()); // 使用打折算法得到的金额为160.0
    
            // 返利算法
            cashStrategy = cashStrategyDecider.decideCashStrategy("return");
            BigDecimal returnResult = cashStrategy.acceptCash(new BigDecimal(500), null, 300d, 100.0d);
            System.out.println("使用返利算法得到的金额为" + returnResult.doubleValue()); // 使用返利算法得到的金额为400.0
        }
    
    }
    
    • 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

    6、策略模式解析

    • 策略模式是一种定义一系列算法的方法,从概念上来看,这些所有的算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
    • 对于打折、返利或者其它算法,其实都是对实际商品收费的一种计算方式,通过继承就可以得到它们的公共功能,这就让它们有了抽象的父类 CashStrategy
    • 策略模式简化了单元测试,每个算法都有自己的类,可以通过自己的接口单独测试,每个算法可以保证它没有错误,修改其中一个也不会影响其它算法
    • 策略模式封装了变化,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性
    • 基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的 Context 对象,相对来说比较好的办法是选择具体实现的职责也由 Context 来承担
  • 相关阅读:
    容器数据卷+MYSQL实战
    小剧场短剧影视小程序源码,附带系统搭建教程
    docker mysql 5.7
    Android gradle动态配置不同打包环境参数值
    二元Weierstrass逼近定理及其证明
    【scikit-learn基础】--『监督学习』之 支持向量机分类
    音视频添 加水印
    java easyexcel 导出多级表头
    基于MxNet实现目标检测-CenterNet【附部分源码及模型】
    Python+Selenium做自动化测试
  • 原文地址:https://blog.csdn.net/qq_53316135/article/details/125465834