设计模式系列之 策略模式

本文详细介绍了策略模式的概念、结构和在实际工程中的应用。通过一个商场打折活动的例子,展示了如何利用策略模式封装不同的折扣策略,如满减、返现和立减,使得新增或修改折扣策略时,代码的改动最小。同时,讨论了如何结合简单工厂模式进一步消除if-else语句,提高代码的可维护性和可扩展性。策略模式适用于处理同一类型问题的多种处理方式,当需要安全封装多种操作或有多个子类需要根据条件选择时,可以考虑使用策略模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。

策略模式强调的是多个策略之间可以互换,这些策略是同一个接口的不同实现类。

模式结构

策略模式涉及三种角色

  1. 抽象策略角色(Strategy)
    使用接口或抽象类实现,包含所有策略的公共行为规范。特别地,若公共行为以包含方法体的方式出现,而不是接口方法的方式,此时只能使用抽象类作为该角色的实现方式;
  2. 环境角色(Context)
    持有一个Strategy引用,供客户端调用;
  3. 具体策略类(ConcreteStrategy)
    实现抽象策略角色定义的公共行为规范;

三者的关系如下图

图一 策略模式UML图

代码实践

假设某商城举办打折活动,分为满减、返现、和立减三种策略,根据不同的情况,分别执行不同的打折活动。策略模式的三种角色代码如下

  • 环境角色

    /**
     * @description: 促销活动--业务逻辑
     * @Date: 2021/10/19 15:23
     */
    public class PromotionActivity {
        //持有策略接口引用
        StrategyInterface strategyInterface;
        //通过构造器 初始化策略接口
        public PromotionActivity(StrategyInterface strategyInterface){
            this.strategyInterface = strategyInterface;
        }
    
        //促销活动自身的业务逻辑
        public void executePromption(){
            strategyInterface.doPromotion();
        }
    }
    
  • 抽象策略角色

    //定义了执行打折的方法
    public interface StrategyInterface {
        void doPromotion();
    }
    
  • 三个具体策略类

    • 策略一

      /**
       * @description: 返现促销-- 一种具体策略实现
       * @Date: 2021/10/19 15:22
       */
      public class FanxianPromotionStrategy implements StrategyInterface{
          @Override
          public void doPromotion() {
              System.out.println("返现促销");
          }
      }
      
    • 策略二

      /**
       * @description: 立减策略-- 一种具体策略实现
       * @Date: 2021/10/19 15:18
       */
      public class LijianPromitionStrategy implements StrategyInterface{
          @Override
          public void doPromotion() {
              System.out.println("立减促销");
          }
      }
      
    • 策略三

      /**
       * @description: 满减促销-- 一种具体策略实现
       * @Date: 2021/10/19 15:21
       */
      public class ManjianPromitionStrategy implements StrategyInterface{
          @Override
          public void doPromotion() {
              System.out.println("满减促销");
          }
      }
      

    完成以上代码编写之后,客户端即可基于环境对象,根据需要执行不同的折扣策略。对于环境角色来说,由于只依赖抽象,不依赖于具体实现。当有新的策略出现时,只需新增一个具体策略类,而不需要修改其他代码,体现了开闭原则。在本文中,当出现新的折扣活动时,只需新建一个实现StrategyInterface接口的具体折扣类,然后在客户端中调用即可,不需要修改其他代码代码。

    	/**
         * 创建环境角色对象,根据业务需要将具体策略对象传入环境角色构造器
         */
        static void baseTest(){
            //使用立减折扣
            PromotionActivity p618 = new PromotionActivity(new LijianPromitionStrategy());
            p618.executePromption();
    
            //使用返现折扣
            PromotionActivity p1111 = new PromotionActivity(new FanxianPromotionStrategy());
            p1111.executePromption();
        }
    

策略模式+简单工厂模式消除if-else选择语句

在实际的工程实践中,可能将选择折扣方式封装为一个方法,代码如下

/**
     * 单独使用策略模式
     * 根据传入的策略key,返回对应的策略模式
     * 这种方式未消除if-else语句,但产生了多个类 扬短避长
     * @param promotionKey
     */
    static PromotionActivity useStrategy(String promotionKey){
        PromotionActivity p = null;
        if("Lijian".equals(promotionKey)){
            p = new PromotionActivity(new LijianPromitionStrategy());
        }else if("Fanxian".equals(promotionKey)){
            p = new PromotionActivity(new FanxianPromotionStrategy());
        }else if("Manjian".equals(promotionKey)){
            p = new PromotionActivity(new ManjianPromitionStrategy());
        }else{
            //promotionKey == null时,返回一个空折扣类;
            // 使程序更加鲁棒,用户体验更好
            //良好的编程习惯
            p = new PromotionActivity(new EmptyPromotionStrategy());
        }
        return p;
    }

客户端调用useStrategy方法,并传入折扣关键字,方法内部根据折扣关键字选择创建对应的折扣策略类。当折扣策略较少且变化较少时,可使用该方式。但若折扣策略经常发生变化,则需要不断的添加if-else选择语句,最终造成该方法的代码块十分臃肿。此时可结合简单工厂模式,消除if-else语句块,简化代码。

  1. 创建策略简单工厂
    简单工厂主要用于存储折扣策略关键字和对应的折扣策略类,并向客户端提供获取策略的方法。代码如下
    /**
     * @description: 生产策略的简单工厂
     * @Date: 2021/10/20 10:57
     */
    public class StrategySimpleFactory {
        private static Map<String,StrategyInterface> PROMOTION_MAP = new HashMap<>();
    
        //存储折扣策略关键字和对应折扣实例
        static {
            PROMOTION_MAP.put("Lijian",new LijianPromitionStrategy());
            PROMOTION_MAP.put("Manjian",new ManjianPromitionStrategy());
            PROMOTION_MAP.put("Fanxian",new FanxianPromotionStrategy());
        }
        
    	//封装null为对象
        private static final StrategyInterface EMPTY_STRATEGY = new EmptyPromotionStrategy();
    
        //私有构造器,不让外部创建该工厂
        private StrategySimpleFactory(){};
    	
    	//供客户端使用
        public static StrategyInterface getStrategy(String promotionKey){
            StrategyInterface si = PROMOTION_MAP.get(promotionKey);
            //return si == null ? new EmptyPromotionStrategy() : si;
            return si == null ? EMPTY_STRATEGY : si;
        }
    }
    
  2. 客户端调用策略
    static void useStategyWithSimpleFactory(String promotionKey){
            PromotionActivity promotionActivity = new PromotionActivity(
            										StrategySimpleFactory.getStrategy(promotionKey));
            promotionActivity.executePromption();
        }
    

从以上代码可以看到,客户端调用时不必再无限度的扩展if-else语句。当有新策略时,只需创建新的策略类,并在简单工厂类中的PROMOTION_MAP存入对应的k-v值即可。
需要注意的时,策略模式提供的是一组可互换位置的策略,而对于具体选择哪种策略,需要客户端自行选择。使用if-else语句是一种选择策略的直接方式,策略模式+简单工厂模式也是一种选择策略的方式,这两种方式都需要客户端提供策略选择的依据(策略实例对应的key),区别在于新增策略时,if-else语句需要直接在客户端中修改代码,而策略模式+简单工厂模式只需在简单工厂模式的PROMOTION_MAP中添加一项数据即可,后者进一步封装了变化的部分,代码也更整洁。

源码分析

java的外部比较器使用了策略模式,具体参看java的两种比较器

适用场景

策略模式强调了策略的互换,因此其适用场景应可以使用该特点。

  • 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时,将具体行为抽象为策略;
  • 需要安全地封装多种同一类型的操作时,将同一类型抽象为策略;
  • 出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。这种情况可配合简单工程模式,去除if-else的判断语句。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值