【设计模式】行为型设计模式之 策略模式学习实践

介绍

策略模式(Strategy),就是⼀个问题有多种解决⽅案,选择其中的⼀种使⽤,这种情况下我们

使⽤策略模式来实现灵活地选择,也能够⽅便地增加新的解决⽅案。⽐如做数学题,⼀个问题的

解法可能有多种;再⽐如商场的打折促销活动,打折⽅案也有很多种,有些商品是不参与折扣活

动要按照原价销售,有些商品打8.5折,有些打6折,有些是返现5元等。

优缺点和场景

优点

  1. 完美符合开闭原则,可以再不修改原系统基础上选择算法行为,或者新增新的算法。
  2. 策略模式,将一类算法进行了抽象,可以将公共部分进行抽离。避免重复代码。
  3. 策略模式对算法的封装,将算法的责任和算法本身分割开,交给不同的对象管理。提供了可替换继承关系的办法,如果不用策略模式,那么环境类,可能自己就有多个子类了,算法和算法的使用还在一起,那就不符合开闭原则。
  4. 可以避免多重条件选择语句。
  5. 算法抽离后,更方便不同的环境类复用。

缺点

  1. 客户端必须知道所有的策略类,并且决定使用哪一个。
  2. 造成了很多具体的策略的类,细小的变化都需要增加新的具体策略类。

使用场景

  1. 系统需要动态的在某些算法里进行选择,那么使用策略模式,用户只要维持一个算法的抽象类对象即可。
  2. 一个对象有很多的行为,避免在一个类里,根据不同的条件进行多重条件判断时。(if(a) 行为a if(b)行为b 将a和b拆成两个具体类。) 可以使用策略模式,将不同的行为,抽象成具体的策略类。
  3. 需要将具体的算法实现,和使用者进行解耦,提高算法的保密性和安全性。

结构

略模式对算法的封装,将算法的责任和算法本身分割开,交给不同的对象管理。使用算法的上下文环境类中,针对抽象的策略类进行编程。符合依赖倒转原则,并且出现了新的算法时,只需要增加一个新的实现即可。

  • **策略(Strategy) **定义所有⽀持算法的公共接⼝。 Context 使⽤这个接⼝来调⽤某 ConcreteStrategy 定义的算法。
  • **策略实现(ConcreteStrategy) **实现了Strategy 接⼝的具体算法
  • **上下⽂环境(Context) **维护⼀个 Strategy 对象的引⽤,⽤⼀个 ConcreteStrategy 对象来装配可定义⼀个接⼝⽅法让 Strategy 访问它的数据

UML类图

基础案例

针对不同商品的打折算法,在引入策略模式前,由一个算法类的方法维护,包含大量的条件转移,并且也不利于维护。

代码下载:strategy.zip

引入策略模式前

引入策略模式前,不同的打折算法的计算过程存在的问题。

  1. 有多重条件选择语句,代码混乱
  2. 不同的算法没有办法进行在别处复用
  3. 新增算法的话,需要修改原来的代码,不符合开闭原则。
java 复制代码
package behavioralPattern.strategy;

import java.text.MessageFormat;

/**
 * 引入策略模式前,不同的打折算法的计算过程
 * 1.有多重条件选择语句,代码混乱
 * 2.不同的算法没有办法进行在别处复用
 * 3.新增算法的话,需要修改原来的代码,不符合开闭原则。
 *
 * @author liuyp
 * @date 2022/09/25
 */
public class BuyGoods {
    private String goods;
    private double price;
    private double finalPrice;
    private String desc;

    public BuyGoods(String goods, double price) {
        this.goods = goods;
        this.price = price;
    }

    public double calculate(String discountType) {
        if ("discount85".equals(discountType)) {
            finalPrice = price * 0.85;
            desc = "该商品可享受8.5折优惠";
        } else if ("discount6".equals(discountType)) {
            finalPrice = price * 0.6;
            desc = "该商品可享受6折优惠";
        } else if ("return5".equals(discountType)) {
            finalPrice = price >= 5 ? price - 5 : 0;
            desc = "该商品可返现5元";
        } else {
            finalPrice = price;
            desc = "对不起,该商品不参与优惠活动";
        }
        System.out.println(MessageFormat.format("您购买的商品为:{0},原价为: {1},{2},最终售卖价格为:{3}", goods, price, desc, finalPrice));
        return finalPrice;
    }
}

引入策略模式

修改步骤

  1. 策略: 引入抽象类,所有的价格计算算法实现该抽象类。
  2. 策略实现:针对原有的if else中的价格计算算法,分别在一个个具体的策略实现类中进行实现。
  3. 环境类:购买商品的类中,只需要维护一个策略抽象类的引用即可,传入不通风策略实现,即可实现不同的打折策略。

抽象打折策略

java 复制代码
/**
 * 策略模式中,对策略的抽象层。
 * 抽象出了公共的描述、价格属性。
 * 定义了需要子类实现的,具体的打折策略方法。
 *
 * @author StoneYu
 * @date 2022/09/25
 */
public abstract class AbstractDiscount {
    protected double finalPrice;
    protected String desc;

    public AbstractDiscount(String desc) {
        this.desc = desc;
    }

    public double getFinalPrice() {
        return finalPrice;
    }

    public void setFinalPrice(double finalPrice) {
        this.finalPrice = finalPrice;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public abstract double discount(double price);
}

拆分各个打折策略的实现

java 复制代码
public class Discount6 extends AbstractDiscount {
    public Discount6() {
        super("该商品可享受6折优惠");
    }

    @Override
    public double discount(double price) {
        finalPrice = price * 0.6;
        return finalPrice;
    }
}

public class Discount85 extends AbstractDiscount {
    public Discount85() {
        super("该商品可享受8.5折优惠");
    }

    @Override
    public double discount(double price) {
        finalPrice = price * 0.85;
        return finalPrice;
    }
}

public class NoDiscount extends AbstractDiscount {
    public NoDiscount() {
        super("对不起,该商品不参与优惠活动");
    }

    @Override
    public double discount(double price) {
        finalPrice = price;
        return finalPrice;
    }
}

public class Return5 extends AbstractDiscount {
    public Return5() {
        super("该商品可返现5元");
    }

    @Override
    public double discount(double price) {
        this.finalPrice = price >= 5 ? price - 5 : 0;
        return finalPrice;
    }
}

修改环境类,只需要维护策略的引用

java 复制代码
/**
 * 策略模式-环境类
 * 使用策略模式优化后的购买商品的方法
 * 1.没有了各种if-else
 * 2.不需要关注算法的具体实现,只需要维护一个策略的抽象类引用。符合依赖倒转原则
 *
 * @author StoneYu
 * @date 2022/09/25
 */
public class BuyGoods {
    private String goods;
    private double price;
    private AbstractDiscount abstractDiscount;

    public BuyGoods(String goods, double price, AbstractDiscount
            abstractDiscount) {
        this.goods = goods;
        this.price = price;
        this.abstractDiscount = abstractDiscount;
    }

    public double calculate() {
        double finalPrice = abstractDiscount.discount(this.price);
        String desc = abstractDiscount.getDesc();
        System.out.println(MessageFormat.format("商品:{0},原价:{1},{2},最 终价格为:{3}", goods, price, desc, finalPrice));
        return finalPrice;
    }
}

Spring中实践

新建策略接口

java 复制代码
public interface DiscountStratege {
    /**
     * 折扣方法
     */
    void discount();

}

具体的策略 1

java 复制代码
/**
 * 具体策略实现1
 *
 * @author LiuYuping
 * @date 2024/03/14 15:14
 */
@Component
public class FullReductionDiscountStratege implements DiscountStratege{
    @Override
    public void discount() {
        System.out.println("满减策略,满一百减100");
    }
}

具体策略 2

java 复制代码
/**
 * 具体策略实现2
 *
 * @author LiuYuping
 * @date 2024/03/14 15:15
 */
@Component
public class WeekDayDiscountStratege implements DiscountStratege{
    @Override
    public void discount() {
        System.out.println("这里是周末满减策略");
    }
}

策略枚举,保存所有策略名称

java 复制代码
public enum DiscountStrategeEnum {
    WEEK_DAY_STRATEGE("fullReductionDiscountStratege","满一百减一百"),
    FULL_REDUCTION("weekDayDiscountStratege","周末满减策略");

    String concernedStrategeBeanId;
    String strategeName;

    DiscountStrategeEnum(String concernedStrategeBeanId, String strategeName) {
        this.concernedStrategeBeanId = concernedStrategeBeanId;
        this.strategeName = strategeName;
    }

    public String getConcernedStrategeBeanId() {
        return concernedStrategeBeanId;
    }

    public String getStrategeName() {
        return strategeName;
    }
}

策略 Context 保存所有策略 Bean 示例

java 复制代码
@Service
public class DiscountStrategeContext {
    @Autowired
    private Map<String,DiscountStratege> allDiscountStrategeMap;


    /**
     * 获取指定的策略
     *
     * @param discountStrategeEnum 折扣策略枚举
     * @return {@link DiscountStratege}
     */
    public DiscountStratege getStratege(DiscountStrategeEnum discountStrategeEnum){
        return allDiscountStrategeMap.get(discountStrategeEnum.getConcernedStrategeBeanId());
    }

}

用例

在需要使用策略的地方,按需注入指定类型的策略对象,新增策略时不需要修改原有代码

java 复制代码
@SpringBootTest
class DemoApplicationTests {

    @Autowired
    DiscountStrategeContext discountStrategeContext;

    @Test
    void testDiscountStratege() {
        DiscountStratege stratege = discountStrategeContext.getStratege(DiscountStrategeEnum.WEEK_DAY_STRATEGE);
        stratege.discount();
    }

}
相关推荐
梓仁沐白14 小时前
【Agent 设计模式】多智能体协作
microsoft·设计模式·php
周航宇JoeZhou14 小时前
JP3-2-1-MyItem项目简介
java·ai·springboot·环境搭建·项目·管理·springai
努力学习的廖同学14 小时前
前端进阶-设计模式
设计模式
剧号15 小时前
互联网大厂Java面试全解析:Java SE 11, Spring Boot及微服务实战问答
java·微服务·面试·kafka·kubernetes·springboot·分布式系统
Eric Woo X15 小时前
macOS 旧版本无法启动 Steam ?一条命令恢复运行(2026年7月实测)
macos·策略模式
workflower2 天前
情境感知系统
人工智能·机器学习·设计模式·自然语言处理·机器人
咖啡八杯2 天前
设计模式中的原则
设计模式·接口隔离原则·里氏替换原则·开闭原则·合成复用原则·单一职责原则
安冬的码畜日常2 天前
【工欲善其事】深入理解 Node.js 带并发上限的异步任务批量执行逻辑
javascript·设计模式·node.js·ai编程·异步编程·并发执行
AI大法师2 天前
一个机场如何被做成 IP 场景:宝可梦机场的设计方法总结
大数据·人工智能·设计模式·新媒体运营
晚安code2 天前
干掉成山的 if-else:工厂造、策略选,一文讲透两个模式的配合
后端·设计模式