你有没有过这种纠结时刻?点外卖时看满减:「满30减5」「满50减15」「满100减30」,到底选哪个更划算?或者打车时:「快车」「特惠快车」「拼车」,不同车型价格算法不一样……
今天要讲的策略模式,就是专门解决这种“多选一、动态切换”问题的!用生活里“换着法子算钱”的例子,保证你5分钟懂。
一、生活实例:点外卖凑满减的“算钱游戏”
假设你要点38元的外卖,平台有3种优惠策略:
- 策略A:满30减5(实付33元)
- 策略B:满50减15(不够50,用不了)
- 策略C:无门槛减2(实付36元)
这时候你会选哪个?显然策略A最划算。但如果订单金额变成55元,策略B(减15)会比策略A(减5)更划算。
问题来了:如果每次订单金额变化,都要手动改代码里的“减5”“减15”,是不是很麻烦?比如:
// 烦死!每次金额变了,都要改这里的数字
int finalPrice = 0;
if (amount >= 50) {
finalPrice = amount - 15;
} else if (amount >= 30) {
finalPrice = amount - 5;
} else {
finalPrice = amount - 2;
}
这时候,策略模式就像个“优惠计算器”——把每种优惠的算法单独封装成“小工具”,需要哪个就调用哪个,灵活得很!
二、Java代码示例:用策略模式实现“自动选最优优惠”
咱们用代码模拟外卖平台的优惠计算,感受策略模式的威力。
步骤1:定义“策略接口”——所有优惠的“统一模板”
不管哪种优惠,核心都是“根据订单金额算最终价格”。所以先定义一个策略接口:
// 优惠策略接口(所有具体优惠的“模板”)
public interface DiscountStrategy {
// 计算最终价格的方法(参数是订单原价)
int calculate(int originalPrice);
}
步骤2:实现具体策略——每种优惠的“专属算法”
每种优惠(满减、无门槛)都实现这个接口,把自己的算法写进去。
策略A:满30减5
// 满30减5的优惠策略
public class Full30Discount implements DiscountStrategy {
@Override
public int calculate(int originalPrice) {
if (originalPrice >= 30) {
return originalPrice - 5; // 减5元
}
return originalPrice; // 不够30,不能用
}
}
策略B:满50减15
// 满50减15的优惠策略
public class Full50Discount implements DiscountStrategy {
@Override
public int calculate(int originalPrice) {
if (originalPrice >= 50) {
return originalPrice - 15; // 减15元
}
return originalPrice; // 不够50,不能用
}
}
策略C:无门槛减2
// 无门槛减2元的优惠策略(谁都能用)
public class NoThresholdDiscount implements DiscountStrategy {
@Override
public int calculate(int originalPrice) {
return originalPrice - 2; // 直接减2元
}
}
步骤3:定义“策略上下文”——负责“选策略+计算”
我们需要一个“工具类”,帮我们选择当前适用的策略,并调用它的算法。这个类叫DiscountContext
:
// 优惠策略上下文(负责选策略、算价格)
public class DiscountContext {
// 当前使用的策略(默认是无门槛)
private DiscountStrategy currentStrategy = new NoThresholdDiscount();
// 动态切换策略(比如根据订单金额自动选最优)
public void setStrategy(DiscountStrategy strategy) {
this.currentStrategy = strategy;
}
// 计算最终价格(调用当前策略的算法)
public int computeFinalPrice(int originalPrice) {
return currentStrategy.calculate(originalPrice);
}
}
步骤4:客户端调用——像“点菜”一样选策略
现在,客户端(比如你的外卖APP)可以根据订单金额,动态选择最优策略:
public class Client {
public static void main(String[] args) {
// 创建策略上下文
DiscountContext context = new DiscountContext();
// 场景1:订单38元(选满30减5)
int order1 = 38;
context.setStrategy(new Full30Discount()); // 切换到策略A
int finalPrice1 = context.computeFinalPrice(order1);
System.out.println("38元订单,满30减5后价格:" + finalPrice1); // 输出33元
// 场景2:订单55元(选满50减15)
int order2 = 55;
context.setStrategy(new Full50Discount()); // 切换到策略B
int finalPrice2 = context.computeFinalPrice(order2);
System.out.println("55元订单,满50减15后价格:" + finalPrice2); // 输出40元
// 场景3:订单15元(只能用无门槛减2)
int order3 = 15;
context.setStrategy(new NoThresholdDiscount()); // 切换到策略C
int finalPrice3 = context.computeFinalPrice(order3);
System.out.println("15元订单,无门槛减2后价格:" + finalPrice3); // 输出13元
}
}
输出结果:
38元订单,满30减5后价格:33
55元订单,满50减15后价格:40
15元订单,无门槛减2后价格:13
示意图:策略模式的“工作流程”
客户端 → 选择策略(如满30减5) → 策略上下文调用该策略的算法 → 返回最终价格
三、策略模式的“使用说明书”
1. 适用场景
策略模式最适合这种“有多个同类算法,需要动态切换”的场景:
- 支付方式:支付宝、微信、信用卡(每种支付方式的验证逻辑不同)。
- 排序算法:冒泡排序、快速排序、归并排序(根据数据量大小选不同算法)。
- 物流计费:普通快递、特快专递、同城闪送(不同配送方式的计费规则)。
2. 优势
- 灵活切换:想换策略?只需要改一行代码(
setStrategy(新策略)
),不用改原来的计算逻辑。 - 代码干净:每种策略的算法独立封装,不会和业务代码“搅成一团”。
- 容易扩展:新增策略(比如“满100减50”)?只需要写一个新的
XXXDiscount
类,不用改已有代码。
3. 不足
- 策略类变多:如果有10种优惠,就要写10个策略类(不过比“if-else地狱”强多了)。
- 客户端需了解策略:客户端(比如你的APP)需要知道有哪些策略可选(但可以通过“策略工厂”隐藏细节)。
四、常见问题Q&A
Q1:策略模式和“if-else”有什么区别?
A:if-else
是把所有逻辑写在一个方法里,新增策略要改代码;策略模式是把每个策略单独封装成类,新增策略只需加新类,符合“开闭原则”(对扩展开放,对修改关闭)。比如上面的例子,如果用if-else
,加“满100减50”要改computeFinalPrice
方法;用策略模式,只需要新建Full100Discount
类,客户端调用即可。
Q2:策略模式需要提前知道所有策略吗?
A:不需要!策略模式的核心是“运行时动态切换”,客户端可以在需要的时候选择任意策略。比如外卖APP可以在用户下单时,根据金额自动选最优策略(比如“满50减15”比“满30减5”更划算)。
Q3:策略对象是每次调用都新建吗?
A:看情况!如果策略无状态(比如“满30减5”的算法不保存任何数据),可以复用同一个实例;如果有状态(比如“用户专属折扣码”),每次调用需要新建实例。
Q4:策略模式和工厂模式能一起用吗?
A:当然可以!如果策略很多,可以用“策略工厂”来管理策略的创建(比如根据订单金额返回对应的策略),客户端只需要和工厂打交道,完全隐藏策略细节。
五、使用策略模式的注意事项
- 策略接口要稳定:策略接口(比如
DiscountStrategy
)一旦定义,尽量不要修改方法(比如加新参数),否则所有具体策略都要改,会很麻烦。 - 策略尽量无状态:具体策略类(比如
Full30Discount
)最好不保存成员变量(比如不记录“当前优惠次数”),否则多个客户端共享同一个实例时可能出问题。 - 别滥用策略模式:如果只有2-3个简单策略,用
if-else
可能更简单;但如果策略经常变或需要动态切换,策略模式更合适。
总结
策略模式的核心是“把变化的部分(算法)封装起来,不变的部分(调用逻辑)稳定下来”。就像你点外卖时,不管选哪种优惠,最后都是“原价-优惠金额”,这个逻辑(调用calculate
方法)是不变的;变化的只是“优惠金额怎么算”(不同策略的实现)。
下次遇到“多选一、动态切换”的需求,记得用策略模式,让你的代码像“自动换挡的汽车”一样灵活!