跳至主要內容

【设计模式】行为型-②策略模式

holic-x...大约 8 分钟设计模式设计模式

【设计模式】行为型-②策略模式

学习核心

  • 代理模式核心
    • 概念:将算法与使用算法的代码进行解耦,提供一种动态选择不同算法的方法
    • 组成:抽象策略(Abstract Strategy)、具体策略(Abstract Strategy)、环境(Context)
      • 抽象策略(Abstract Strategy):定义公共接口或抽象类
      • 具体策略(Concrete Strategy):定义策略的具体实现算法
      • 环境(Context):维护对一个策略对象的引用,负责将客户端的请求委派给具体的策略对象执行
  • 应用场景分析
    • 【营销场景】不同折扣活动策略:例如某个活动设定了不同的优惠券类型,根据相应的类型实施优惠策略,计算折扣后的金额

基本概念

策略模式:将算法与使用算法的代码进行解耦,提供一种动态选择不同算法的方法。客户端不需要关注使用了哪种算法,而是通过提供的环境类来使用所选择的策略

​ 策略模式主要涉及策略、环境两大组成部分:

  • 策略(抽象策略、具体策略实现):
    • 抽象策略(Abstract Strategy):可以是抽象类或接口,定义公共方法
    • 具体策略实现(Concrete Strategy):策略接口实现类,实现具体的方法,调用相应的算法
  • 环境(Context):用于维护对一个策略对象的引用,将客户端请求委派到对应的策略对象进行调用

场景案例demo:对两个数进行加减乘除操作

🧨传统方式

​ 最简单的方式,直接根据操作进行判断操作,然后执行相应业务逻辑即可。

public class MyComputer {
    public static void main(String[] args) {
        int a=10,b=2;
        String operator = "+";
        if(operator.equals("+")){
            System.out.println(a+b);
        }else if(operator.equals("-")){
            System.out.println(a-b);
        }else if(operator.equals("*")){
            System.out.println(a*b);
        }else if(operator.equals("%")){
            System.out.println(a%b);
        }else{
            throw new RuntimeException("指定操作符异常");
        }
    }
}

✨策略模式

​ 基于功能分析,此处策略指的是要对提供的两个数做什么(例如实现什么业务逻辑、调用什么算法),而Context则是指维护一个策略对象,将Client请求委派到对应的策略对象进行调用

  • 策略定义(抽象策略、具体策略实现)
/**
 * 抽象策略(Abstract Strategy):可以是抽象类或接口定义,定义策略的公共接口方法
 */
public interface OperationStrategy {
    public int operate(int a, int b);
}

/**
 * 具体策略:相加操作
 */
public class OperationAdd implements OperationStrategy{
    @Override
    public int operate(int a, int b) {
        return a+b;
    }
}

/**
 * 具体策略:相减操作
 */
public class OperationSubtract implements OperationStrategy{
    @Override
    public int operate(int a, int b) {
        return a-b;
    }
}

/**
 * 具体策略:相乘操作
 */
public class OperationMultiply implements OperationStrategy{
    @Override
    public int operate(int a, int b) {
        return a*b;
    }
}

/**
 * 具体策略:取模操作
 */
public class OperationMod implements OperationStrategy{
    @Override
    public int operate(int a, int b) {
        return a%b;
    }
}
  • 环境定义(context)
/**
 * 环境(context):用于维护一个策略对象引用,将Client请求委派到对应的策略对象进行调度
 */
public class OperationContext {

    // 维护相应的策略对象
    private OperationStrategy operationStrategy;

    // 初始化环境
    public OperationContext(OperationStrategy operationStrategy) {
        this.operationStrategy = operationStrategy;
    }

    // 执行具体策略
    public int execute(int a, int b) {
        return this.operationStrategy.operate(a,b);
    }
}
  • Client 测试
/**
 * 客户端调用
 */
public class Client {
    public static void main(String[] args) {
        // 测试新增操作
        OperationContext context = new OperationContext(new OperationAdd());
        System.out.println(context.execute(1,2));

        // 相减操作
        context = new OperationContext(new OperationSubtract());
        System.out.println(context.execute(1,2));

        // 相乘操作
        context = new OperationContext(new OperationMultiply());
        System.out.println(context.execute(1,2));

        // 取模操作
        context = new OperationContext(new OperationMod());
        System.out.println(context.execute(10,2));
    }
}

-- output
3
-1
2
0

场景案例分析

1.【营销场景】不同折扣活动策略(优惠券策略)

​ 场景说明:在一些营销场景中模拟购买商品时,可能会有各种类型的优惠券,需要根据不同的优惠券类型(满减、直减、折扣、n元购)来进行折算,因此会涉及到多种折算策略。

🧨传统实现方式

​ 传统实现实现方式最简单的就是根据条件分支语句进行设计,根据不同的优惠券类型选择相应的策略方法进行执行

/**
 * 根据不同优惠券类型计算折扣金额:
 * 1-满减、2-直减、3-折扣、4-n元购
 */
public class DiscountService {

    public double discountAmount(int discountType,double discountTypeContent, double skuPrice) {
        double discountAmount = 0;
        if(discountType==1){ // 此时discountTypeContent为满减金额阈值(可以设定满减金额参数,此处设定为满多少就减多少)
            // 达到满减阈值则进行扣减,未达到则维持原价
            discountAmount = skuPrice<discountTypeContent? skuPrice:(skuPrice - discountTypeContent);
        }else if(discountType==2){// 此时discountTypeContent为直减金额
            discountAmount = skuPrice-discountTypeContent;
        }else if(discountType==3){// 此时discountTypeContent为折扣系数(0-1取值)
            discountAmount = skuPrice*discountTypeContent;
        }else if(discountType==4){// 此时discountTypeContent为n元购参数设定
            discountAmount = discountTypeContent;
        }
        // 返回对应折算策略计算后的金额
        return discountAmount;
    }
}

​ 测试demo

/**
 * 优惠券折算活动计算
 */
public class DiscountDemo {

    public static void main(String[] args) {
        DiscountService discountService = new DiscountService();
        // 满300减300
        System.out.println("满减后:" + discountService.discountAmount(1,300,500));
        // 直减100
        System.out.println("直减后:" + discountService.discountAmount(2,100,500));
        // 打三折
        System.out.println("折扣后:" + discountService.discountAmount(3,0.3,500));
        // 0元购
        System.out.println("n元购后:" + discountService.discountAmount(4,0,500));
    }

}

-- output
满减后:200.0
直减后:400.0
折扣后:150.0
n元购后:0.0

​ 基于传统模式实现,可以看到方法的定义其实并不能很好的兼容,例如满减策略需要满减阈值和满减金额两个参数,但是有一些优惠策略并不需要这些参数,所以方法定义中实际上很难兼顾具体实现场景

​ 其次在于上述的方式是通过条件分支语句来进行策略选择,随着功能扩展到后续会使得这个分支变得非常臃肿,不便于扩展

✨策略模式实现

​ 基于上述场景分析,实际上策略即优惠券折算策略,对应的实现即对应不同类型优惠券折算算法。基于此设定使用策略模式进行代码优化

  • 策略 strategy:

    • 抽象策略:IDiscountStrategy 优惠策略接口定义
    • 具体策略:MJDiscountStrategy 满减策略、ZJDiscountStrategy 直减策略、ZKDiscountStrategy 折扣策略、NBDiscountStrategy n元购策略
  • 环境context:

    • DiscountContext:维护一个具体策略对象的引用,将client的请求委派到对应的策略对象进行执行

代码实现参考

  • 策略定义
/**
 * 抽象策略:优惠策略接口定义
 */
public interface IDiscountStrategy {
    public double discountAmount(double strategyContent,double skuPrice);
}

/**
 * 具体策略:满减策略
 */
public class MJDiscountStrategy implements IDiscountStrategy{
    @Override
    public double discountAmount(double strategyContent, double skuPrice) {
        // 满减策略:此处strategyContent为满多少减多少
        return skuPrice<strategyContent?skuPrice:skuPrice-strategyContent;
    }
}

/**
 * 具体策略:直减策略
 */
public class ZJDiscountStrategy implements IDiscountStrategy{
    @Override
    public double discountAmount(double strategyContent, double skuPrice) {
        // 直减策略:此处strategyContent为直减金额
        return skuPrice-strategyContent;
    }
}

/**
 * 具体策略:折扣策略
 */
public class ZKDiscountStrategy implements IDiscountStrategy{
    @Override
    public double discountAmount(double strategyContent, double skuPrice) {
        // 折扣策略:此处strategyContent为折扣系数
        return skuPrice*strategyContent;
    }
}

/**
 * 具体策略:n元购策略
 */
public class NBDiscountStrategy implements IDiscountStrategy{
    @Override
    public double discountAmount(double strategyContent, double skuPrice) {
        // n元购策略:此处strategyContent为n元购配置
        return strategyContent;
    }
}
  • 环境定义
/**
 * 环境:维护一个策略引用,用于将客户端请求委派到具体策略对象进行执行
 */
public class DiscountContext {

    // 引用策略对象
    private IDiscountStrategy discountStrategy;

    // 初始化环境(初始化策略)
    public DiscountContext(IDiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    // 执行策略
    public double execStrategy(double strategyContent,double skuPrice){
        return this.discountStrategy.discountAmount(strategyContent,skuPrice);
    }
}
  • 客户端测试
/**
 * 策略模式客户端测试
 */
public class DiscountClient {
    public static void main(String[] args) {

        // 初始化环境并执行策略
        DiscountContext discountContext = null;

        // 满300减300
        discountContext = new DiscountContext(new MJDiscountStrategy());
        System.out.println("满减后:" + discountContext.execStrategy(300,500));

        // 直减100
        discountContext = new DiscountContext(new ZJDiscountStrategy());
        System.out.println("直减后:" + discountContext.execStrategy(100,500));

        // 打三折
        discountContext = new DiscountContext(new ZKDiscountStrategy());
        System.out.println("折扣后:" + discountContext.execStrategy(0.3,500));

        // 10元购
        discountContext = new DiscountContext(new NBDiscountStrategy());
        System.out.println("n元购后:" + discountContext.execStrategy(10,500));
    }
}

-- output
满减后:200.0
直减后:400.0
折扣后:150.0
n元购后:10.0

🎉🎉代码进一步优化(泛型引入,灵活适配不同策略的参数设定)

​ 从上述代码实现来看,每种优惠券计算的参数strategyContent好像都被限定了一样,参数设定并不灵活。因此基于上述设定进一步引入泛型,可以灵活适配策略执行所需参数类型并进行相应处理,提高策略实现的可扩展度

  • 策略定义
/**
 * 策略定义:接收泛型参数
 * @param <T>
 */
public interface Strategy<T> {
    // 定义公共方法:计算折算金额
    public double discountAmount(T t, double skuPrice);
}

/**
 * 满减策略
 */
public class MJStrategy implements Strategy <Map<String,String>>{
    @Override
    public double discountAmount(Map<String, String> map, double skuPrice) {
        // map 中定义了相应的满减策略(满减金额阈值、满减金额)
        double mj = Double.parseDouble(map.get("mj"));
        double amount = Double.parseDouble(map.get("amount"));
        return skuPrice > mj ? (skuPrice - amount) : skuPrice;
    }
}

/**
 * 直减策略
 */
public class ZJStrategy implements Strategy <Double>{
    @Override
    public double discountAmount(Double amount, double skuPrice) {
        return skuPrice - amount;
    }
}

/**
 * 折扣策略
 */
public class ZKStrategy implements Strategy <Double>{
    @Override
    public double discountAmount(Double discountFactor, double skuPrice) {
        return skuPrice * discountFactor;
    }
}

/**
 * N元购策略
 */
public class NYGStrategy implements Strategy <Double>{
    @Override
    public double discountAmount(Double discountAmount, double skuPrice) {
        return discountAmount;
    }
}
  • 环境定义
/**
 * 环境定义
 */
public class Context<T> {

    // 维护策略对象引用
    private Strategy<T> strategy;

    // 初始化策略对象
    public Context(Strategy<T> strategy) {
        this.strategy = strategy;
    }

    // 执行策略
    public double execStrategy(T t,double skuPrice) {
        return this.strategy.discountAmount(t,skuPrice);
    }
}
  • 客户端测试
/**
 * 策略模式客户端测试demo
 */
public class Client {
    public static void main(String[] args) {

        // 满减
        Map<String,String> mjMap = new HashMap<>();
        mjMap.put("mj","300");
        mjMap.put("amount","50");
        Context<Map<String,String>> mjContext = new Context<>(new MJStrategy());
        System.out.println("满减后:" + mjContext.execStrategy(mjMap,500));

        // 直减
        Context<Double> zjContext = new Context<>(new ZJStrategy());
        System.out.println("直减后:" + zjContext.execStrategy(Double.valueOf(100),500));

        // 折扣 打三折
        Context<Double> zkContext = new Context<>(new ZKStrategy());
        System.out.println("折扣后:" + zkContext.execStrategy(Double.valueOf(0.3),500));

        // N 元购
        Context<Double> nygContext = new Context<>(new NYGStrategy());
        System.out.println("N元购后:" + nygContext.execStrategy(Double.valueOf(10),500));
    }
}
-- output
满减后:450.0
直减后:400.0
折扣后:150.0
N元购后:10.0

2.【OJ】

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3