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

介绍

策略模式(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();
    }

}
相关推荐
lxyzcm11 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千11 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)12 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
诸葛悠闲13 小时前
设计模式——桥接模式
设计模式·桥接模式
捕鲸叉17 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~18 小时前
框架专题:设计模式
设计模式·框架
先睡18 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
waicsdn_haha18 小时前
Postman最新详细安装及使用教程【附安装包】
测试工具·api·压力测试·postman·策略模式·get·delete
Damon_X1 天前
桥接模式(Bridge Pattern)
设计模式·桥接模式
越甲八千1 天前
重温设计模式--享元模式
设计模式·享元模式