• 设计模式之策略模式


    概述

    策略模式(Strategy)(对象行为型):
    是定义了一组算法或策略,将每个算法/策略封装起来,可以相互替换,实现一个功能有个多个算法或策略,然后根据不同的环境和条件选择不同的算法或者策略来实现某功能。

    本质:分离算法,选择实现,简单说就是解决一类问题,用户可以根据情况任选某一种算法解决,同时可以很方便的增加或者去除某个算法。

    在这里插入图片描述

    模式解析
    此模式涉及到三个角色:
    (1)抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
    (2)具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
    (3)环境(Context)角色:持有一个Strategy的引用。

    应用场景:出行选择交通策略、用户支付选择、商品针对不同情况的打折方式

    Sring中的应用:springboot启动加载时实例化对象

    应用示例

    以超市针对不同的会员,计算出每件商品的折扣价。

    1. 传统方式

    传统方式是,根据不同策略写if()else{}代码,如下:

     public Double calculatePrice(int cardLevel, Double price){
            if(cardLevel == 1){
                return price*0.9;
            }else if(cardLevel == 2){
                return price*0.85;
            }else if(cardLevel == 2){
                return price*0.8;
            }else{
                ....
            }
            return price;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. 应用策略模式

    2.1 定义一个折扣策略接口
    /**
     * 定义折扣策略
     */
    public interface DiscountStrategy {
    
        public Double doCalculatePrice(double price);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.2 创建具体折扣的实现类

    以下设计两种方式,银卡会员和金卡会员的折扣

    /**
     * 折扣策略具体实现-银卡折扣价计算:9折
     */
    public class SilverCardDiscountPrice implements DiscountStrategy{
    
        @Override
        public Double doCalculatePrice(double price) {
            return price*0.9;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    /**
     * 折扣策略具体实现-金卡折扣价结算:85折
     */
    public class GoldenCardDiscountPrice implements DiscountStrategy{
    
        @Override
        public Double doCalculatePrice(double price) {
            return price*0.85;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2.3 创建持有策略的具体对象
    public class Context {
    
        private DiscountStrategy strategy;
    
        //通过构造函数注入,此外可通过注解方式等其他方式
        public Context(DiscountStrategy strategy){
            this.strategy = strategy;
        }
    
        public Double calculatePriceSum(double price){
            return strategy.doCalculatePrice(price);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    2.4 调用实现
    public class StrategyTest {
    
        public static void main(String[] args) {
    
            //具体策略
            DiscountStrategy goldenStrategy = new GoldenCardDiscountPrice();
            DiscountStrategy silverStrategy = new SilverCardDiscountPrice();
    
            //打印出不同会员里某商品折扣价格
            int price = 300;
            Context context1 = new Context(goldenStrategy);
            System.out.println("原价"+ price +",金卡会员折扣价:" + context1.calculatePriceSum(price));
    
            Context context2 = new Context(silverStrategy);
            System.out.println("原价"+ price +",银卡会员折扣价:" + context2.calculatePriceSum(price));
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    2.5 返回结果
        原价300,金卡会员折扣价:255.0
        原价300,银卡会员折扣价:270.0
    
    • 1
    • 2

    总结

    以上为一个折扣价的简单实现,常规项目中,除了定义并实现一个个策略很重要,其根据不同的场景选择不同的策略也值得思考。以上例子简单通过构造函数注入,也可通过setter方式注入,也可以通过注解方式,也可以同先做项目一样通过定义一个匹配器,根据数据匹配到不同的策略等其他多种方式,通常项目中会结合其他设计模式混合使用。

    优点

    1. 策略模式中其算法可以自由切换,减少ifelse,避免臃肿,及避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护)
    2. 扩展性良好,增加一个策略只需实现接口即可
    3. 符合设计的开闭原则,对客户隐藏具体策略 (算法) 的实现细节,彼此完全独立。

    缺点

    1. 策略类数量会增多,每个策略都是一个类,复用的可能性很小
    2. 策略类都需要对外暴露,用户端必须知道所有的策略类,并自行决定使用哪一个策略类。
    3. context在使用这些策略类的时候,这些具体实现策略类由于继承了策略接口,所以有些数据可能用不到,但是依然被初始化。

    author:yana

  • 相关阅读:
    C++基础第9章:序列与关联容器(2)——序列容器
    机器学习之偏差与方差的区别
    golang笔记18--go并发多线程
    基于LINUX的TCP协WireShark抓包分析
    Windows运维相关经验技巧
    CSS属性 - box-sizing
    微信扫一扫 - 实现签到功能 - 思路
    Django之同时新增数据到两个数据库表与同时返回两个表的数据(插拔式)
    2.springboot代理调用
    springboot+基于web的传染病信息管理系统的设计与实现 毕业设计-附源码221124
  • 原文地址:https://blog.csdn.net/vipshop_fin_dev/article/details/126828452