笔头云 笔头云
首页
设计模式
SQL教程
Redis
归档
关于
友链

笔头云

非淡泊无以明志,非宁静无以致远。
首页
设计模式
SQL教程
Redis
归档
关于
友链
  • 设计原则
  • 设计模式
  • 单例模式
  • 工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 代理模式
  • 装饰器模式
  • 门面模式
  • 桥接模式
  • 享元模式
  • 组合模式
  • 策略模式
    • 定义
    • 结构
    • 优点
    • 缺点
    • 应用场景
    • 示例代码1
    • 示例代码2
  • 模板方法模式
  • 观察者模式
  • 责任链模式
  • 状态模式
  • 迭代器模式
  • 访问者模式
  • 中介模式
  • 命令模式
  • 解释器模式
  • 备忘录模式
  • 设计模式
笔头云
2023-08-24
目录

策略模式

# 策略模式

在现实生活中常常遇到实现某种目标可以选择多种策略的情况,例如,出行方式可以选择公交、地铁、自行车、步行或开车等,超市促销可以采用打折、满送、满减等方法。
在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据不同条件选择不同的算法或者策略来完成该功能。
如果使用条件语句实现,不但使条件语句变得很复杂,而且不易维护,违背开闭原则。如果采用策略模式就能很好的解决该问题。

# 定义

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。 img

策略模式与状态模式的区别

策略模式(Strategy Pattern)与状态模式(State Pattern)的主要区别在于它们关注的问题领域和解决的问题类型。策略模式关注选择和切换不同的算法或策略,状态模式关注对象内部的状态变化。
策略模式关注选择和切换不同的算法或策略,它通过封装算法或策略为独立类来实现策略的选择和切换。而状态模式关注对象的内部状态的变化和行为的变化,它通过封装状态为独立类来实现状态之间的切换。

策略模式与访问者模式的区别

策略模式用于在运行时选择算法或策略,将算法封装在策略类中。访问者模式通过访问者对象用于对元素进行不同的操作,将操作封装在访问者类中。

# 结构

策略接口(Strategy Interface): 策略接口定义了所有具体策略类(算法类)所需实现的方法。它通常是一个接口或抽象类,声明了算法的公共操作。
具体策略类(Concrete Strategies): 具体策略类实现了策略接口,提供了具体的算法实现。每个具体策略类都封装了一种特定的算法,并提供了算法的具体实现。
环境类(Context): 环境类持有一个策略接口的引用,并在运行时根据需要调用具体策略类的算法。环境类将客户端与具体策略类解耦,客户端只需要与环境类进行交互,而无需直接与具体策略类交互。

策略模式

- img: https://bitouyun.com/images/design-pattern/strategy2.png
  link: https://bitouyun.com/images/design-pattern/strategy2.png
  name: 策略模式
1
2
3

# 优点

易于扩展和维护:由于策略模式将每个算法封装成独立的策略类,因此添加新的策略只需要新增一个具体策略类,符合开闭原则。同时,修改现有的策略也只需要修改相应的具体策略类,不会对其他策略产生影响,降低了代码的耦合性,使得系统更易于扩展和维护。
灵活性高:策略模式通过将算法封装成独立的策略类,使得这些策略可以在运行时动态替换,客户端可以根据需要选择不同的策略,提供了更高的灵活性。这样,系统可以根据具体情况选择最合适的算法,实现定制化的行为。
可复用性好:策略模式将算法封装成独立的策略类,这些策略类可以被多个客户端共享和复用。不同的客户端可以使用相同的策略类,避免了代码的重复编写,提高了代码的复用性。
降低算法使用与算法实现之间的耦合度:策略模式通过环境类来与具体策略类进行交互,客户端只需要与环境类进行交互,而无需直接与具体策略类交互。这样,策略的选择和使用与具体策略类的实现相互解耦,降低了客户端与具体策略类之间的耦合度。

# 缺点

增加了类的数量:使用策略模式会引入多个策略类,可能会增加系统中的类的数量。如果策略较多,可能会导致类的数量增加过多,增加了代码的复杂性和理解难度。
客户端需要了解不同的策略:客户端需要了解不同的策略类,以便在运行时选择合适的策略。这要求客户端具有一定的了解和判断能力,增加了客户端的复杂性。客户端选择了不合适的策略,可能会导致系统的性能或行为问题。

# 应用场景

一个系统需要根据不同场景,切换不同的实现逻辑,可将每个算法封装到策略类中;
系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时;
多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为;
存在大量if else条件判断。

# 示例代码1

提示

上下文角色(环境类)中如果策略数量小可以使用switch条件分支切换策略,策略数量多可以从IOC容器中获取策略对象。
SpringBoot下要从容器中获取对象实例。

场景1

超市促销活动:正常收费、打折促销、满减活动,根据不同的活动计算收费金额。客户端只需跟环境类交互,环境类中添加工厂模式,根据传入不同的策略枚举值创建具体的策略对象,计算对应的结果。

    /**
     * 上下文角色(环境类)
     */
    public class CashContext {
        // 现金抽象类(策略接口)
        private CashSuper cashSuper;
    
        /**
         * 构造方法传入价格策略
         *
         * @param cashSuper
         */
        public CashContext(CashSuper cashSuper) {
            this.cashSuper = cashSuper;
        }
    
        /**
         * 根据收费类型生成具体收费策略
         * 策略与简单工程结合
         *
         * @param type 0:正常收费 1:满减收费 2:打折收费
         */
        public CashContext(String type) {
            switch (type) {
                case "0":
                    CashNormal cashNormal = new CashNormal();  // springboot下要从容器中获取对象实例
                    // CashNormal cashNormal = (CashNormal) applicationContext.getBean("cashNormal");
                    cashSuper = cashNormal;
                    break;
                case "1":
                    CashReturn cashReturn = new CashReturn(300, 100);
                    cashSuper = cashReturn;
                    break;
                case "2":
                    CashRebate cashRebate = new CashRebate(0.8);
                    cashSuper = cashRebate;
                    break;
            }
        }
    
        /**
         * 获取实际收费金额
         *
         * @param money 原价
         * @return 实际收取金额
         */
        public double getResult(double money) {
            return cashSuper.acceptCash(money);
        }
    }
    
    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
    46
    47
    48
    49
    50
    /**
     * 现金收费抽象类
     * 策略接口,定义了所有策略类(算法类)需要实现的方法,通常是一个接口或抽象类,声明所有算法的公共操作。
     */
    public abstract class CashSuper {
        /**
         * 收取现金
         *
         * @param money 原价
         * @return 实际收取金额
         */
        public abstract double acceptCash(double money);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
     * 正常收费类
     * 具体策略类
     */
    public class CashNormal extends CashSuper {
    
        @Override
        public double acceptCash(double money) {
            return money;
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
     * 折扣收费类
     * 具体策略类
     */
    public class CashRebate extends CashSuper {
        // 折扣
        private double moneyRebate;
    
        public CashRebate(double moneyRebate) {
            this.moneyRebate = moneyRebate;
        }
    
        @Override
        public double acceptCash(double money) {
            return money * moneyRebate;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
     * 返利收费类
     * 具体策略类
     * 满300减100
     */
    public class CashReturn extends CashSuper {
    
        // 返利条件 ex:300
        private double moneyCondition;
    
        public CashReturn(double moneyCondition, double moneyReturn) {
            this.moneyCondition = moneyCondition;
            this.moneyReturn = moneyReturn;
        }
    
        // 返利金额 ex:100
        private double moneyReturn;
    
        @Override
        public double acceptCash(double money) {
            double result = money;
            if (money > moneyCondition) {
                // 每满300-100 结果 = 金额 - 返利条件倍数 * 返利金额
                result = money - Math.floor(money / moneyCondition) * moneyReturn;
            }
            return result;
        }
    }
    
    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 Client {
    
        public static void main(String[] args) {
    
            double total = 500.0;
            // 创建上下文角色(环境类Context)
            CashContext cashNormal = new CashContext("0");
            double normalResult = cashNormal.getResult(total);
            System.out.println("正常收费:" + normalResult);
    
            CashContext cashContextReturn = new CashContext("1");
            double cashReturnResult = cashContextReturn.getResult(total);
            System.out.println("返现收费:" + cashReturnResult);
    
            CashContext cashRebate = new CashContext("2");
            double rebateResult = cashRebate.getResult(total);
            System.out.println("折扣收费:" + rebateResult);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Make sure to add code blocks to your code group

    # 示例代码2

    场景2

    投资调查问卷,需要统计问卷的总分和每个维度的平均分,抽象为计算原始总分和计算平均分两种策略,使用时根据传入的枚举值决定具体使用哪种策略进行计算。

        /**
         * 上下文角色(环境类)
         */
        @Slf4j
        @Service
        public class CalcScoreContext {
      
          @Resource
          private ApplicationContext applicationContext;
          
          /**
           * 计算分数
           * @param calculateTypeEnum 类型
           * @return 计算结果
           */
          public CalculateResult getScore(CalcTypeEnum calculateTypeEnum) {
              AbstractCalcScore abstractCalculateScore = (AbstractCalcScore) applicationContext.getBean(calculateTypeEnum.getBean());
              // 计算分数
              return abstractCalculateScore.calculate();
        }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      /**
      * 抽象策略类
      * 定义了所有具体策略类(算法类)所需实现的方法
      */
      public abstract class AbstractCalcScore {
      
          /**
           * 计算分数
           * @return 分数
           */
          abstract CalculateResult calculate();
      
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      /**
       *  具体策略类,计算原始总分
       */
      @Slf4j
      @Service
      public class OriginalScore extends AbstractCalcScore {
      
          public CalculateResult calculate() {
              log.debug("计算原始分");
              return new CalculateResult(90.0);
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      /**
       *  具体策略类,计算平均分
       */
      @Slf4j
      @Service
      public class AverageScore extends AbstractCalcScore {
      
          @Override
          CalculateResult calculate() {
              log.debug("计算平均分", calcCondition);
              return new CalculateResult(80.0);
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @Data
      @AllArgsConstructor
      public class CalculateResult implements Serializable {
          private Double score;
      }
      
      1
      2
      3
      4
      5
      @Getter
      @AllArgsConstructor
      public enum CalcTypeEnum {
          ORIGINAL(1, "原始分", "originalScore"),
          AVERAGE(2, "平均分", "averageScore");
      
          private Integer type;
          private String name;
          private String bean;
      
          public static CalcTypeEnum fromType(Integer type) {
              for (CalcTypeEnum calculateTypeEnum : CalcTypeEnum.values()) {
                  if (calculateTypeEnum.type.equals(type)) {
                      return calculateTypeEnum;
                  }
              }
              return null;
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      @Slf4j
      @Service
      public class CalculateService{
          // 上下文角色(环境类)
          @Resource
          private CalcScoreContext calcScoreContext;
         
          // 客户端调用方法时,传入对应计算枚举
          @Override
          public void execCalculate(CalcTypeEnum calcTypeEnum) {
             // 计算分数
              CalculateResult calculateResult = this.calcScoreContext.getScore(calcTypeEnum);        
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // Make sure to add code blocks to your code group
      #行为型模式#设计模式
      上次更新: 2023/12/07, 08:50:59
      组合模式
      模板方法模式

      ← 组合模式 模板方法模式→

      最近更新
      01
      FRP内网穿透docker部署 工具
      05-07
      02
      Office Util办公工具 工具
      01-14
      03
      Git常用命令
      01-16
      更多文章>
      Theme by Vdoing | Copyright © 2023-2025 鲁ICP备2023014898号 公安备案号:37020302372159
      • 跟随系统
      • 浅色模式
      • 深色模式
      • 阅读模式
      ×