【设计模式】行为型-②策略模式
...大约 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