策略模式笔记
免费版Java学习笔记(38w字)链接:https://www.yuque.com/aoyouaoyou/sgcqr8
免费版Java面试题(28w字)链接:https://www.yuque.com/aoyouaoyou/wh3hto
完整版可在小红书搜索:遨游qk0
策略模式是行为型设计模式,核心思想是“定义一系列可替换的算法,将每个算法封装为独立类,使算法可独立于使用它的客户端变化”,通过上下文类动态选择算法,消除冗余的条件判断(if-else/switch),提高代码扩展性。适用于存在多种方案、需动态切换的场景(如支付方式、排序算法、折扣规则)。
此处为语雀内容卡片,点击链接查看:https://www.yuque.com/aoyouaoyou/pw1w5m/ylswgg47et43441q
此处为语雀内容卡片,点击链接查看:https://www.yuque.com/aoyouaoyou/pbz18g/cho47q57wrh23fg4
代码位置:【demo1的下代码是本节代码;demo2下的代码是面试笔记中策略模式的代码】




一、概念
1. 定义
策略模式(Strategy Pattern)原始定义:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法可以独立于使用它的客户端而变化。
2. 生活场景类比
电商平台“商品折扣策略”:平台针对不同用户(新用户、会员、老用户)和不同活动(满减、打折、无折扣)提供多种折扣方案。
- 折扣策略(算法):新用户满100减30、会员9折、老用户无折扣、节日双倍积分;
- 客户端(用户下单):无需关心折扣计算细节,只需选择对应的用户类型或活动,系统自动匹配折扣策略。
通过策略模式,每种折扣方案封装为独立策略类,新增折扣(如优惠券抵扣)时无需修改原有代码,直接新增策略类即可。
类似场景还有:出行路线规划(公交、地铁、自驾)、文件压缩算法(ZIP、RAR、7Z)、数据校验规则(手机号校验、邮箱校验)。
3. 开发痛点
- 条件判断冗余:多个算法通过if-else/switch判断选择,代码臃肿且难以维护;
- 扩展性差:新增算法需修改原有条件判断代码,违反开闭原则;
- 算法耦合度高:所有算法集中在一个类中,修改一个算法可能影响其他算法;
- 可读性低:复杂条件判断和算法逻辑混合,代码难以理解。
二、角色
| 角色名称 | 核心职责 | 示例(折扣策略场景) |
| 抽象策略(Strategy) | 定义算法的统一接口,声明所有具体策略需实现的方法 | 折扣策略接口( |
| 具体策略(ConcreteStrategy) | 实现抽象策略接口,封装具体的算法逻辑 | 新用户折扣( |
| 上下文(Context) | 持有抽象策略的引用,提供接口供客户端设置策略,动态选择并执行算法 | 订单上下文( |
三、模式实现(电商折扣策略场景)
场景说明
实现电商订单折扣计算功能,支持以下策略:
- 新用户策略:订单满100减30,不满100无折扣;
- 会员策略:订单总价9折,无门槛;
- 老用户策略:无折扣,但赠送10积分;
- 节日策略:订单满200减50,同时享9.5折(叠加优惠)。
要求支持动态切换策略,新增策略无需修改原有代码。
UML图和流程图



1. 抽象策略接口
import java.math.BigDecimal;
/**
* 抽象策略:折扣策略接口
* 定义折扣计算的统一方法
*/
public interface DiscountStrategy {
// 计算折扣后价格:参数为订单原价,返回折扣后价格
BigDecimal calculate(BigDecimal originalPrice);
// 获取策略名称(用于日志输出)
String getStrategyName();
}
2. 具体策略实现
/**
* 具体策略1:新用户折扣(满100减30)
*/
public class NewUserDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
BigDecimal threshold = new BigDecimal("100");
BigDecimal discount = new BigDecimal("30");
if (originalPrice.compareTo(threshold) >= 0) {
return originalPrice.subtract(discount);
}
return originalPrice;
}
@Override
public String getStrategyName() {
return "新用户满100减30";
}
}
/**
* 具体策略2:会员折扣(9折)
*/
public class MemberDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
BigDecimal rate = new BigDecimal("0.9");
return originalPrice.multiply(rate).setScale(2, BigDecimal.ROUND_HALF_UP);
}
@Override
public String getStrategyName() {
return "会员9折";
}
}
/**
* 具体策略3:老用户折扣(无折扣,送10积分)
*/
public class OldUserDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
System.out.println("老用户福利:赠送10积分");
return originalPrice;
}
@Override
public String getStrategyName() {
return "老用户无折扣送积分";
}
}
/**
* 具体策略4:节日折扣(满200减50+9.5折)
*/
public class FestivalDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
// 第一步:满200减50
BigDecimal threshold = new BigDecimal("200");
BigDecimal subtract = new BigDecimal("50");
BigDecimal priceAfterSubtract = originalPrice.compareTo(threshold) >= 0
? originalPrice.subtract(subtract) : originalPrice;
// 第二步:9.5折
BigDecimal rate = new BigDecimal("0.95");
return priceAfterSubtract.multiply(rate).setScale(2, BigDecimal.ROUND_HALF_UP);
}
@Override
public String getStrategyName() {
return "节日满200减50+9.5折";
}
}
3. 策略工厂(消除条件判断)
import java.util.HashMap;
import java.util.Map;
/**
* 策略工厂:管理所有策略,根据策略类型获取对应策略(消除if-else)
*/
public class DiscountStrategyFactory {
// 存储策略:key为策略类型,value为策略对象
private static final Map STRATEGY_MAP = new HashMap<>();
// 静态初始化策略(也可通过配置文件+反射初始化,完全符合开闭原则)
static {
STRATEGY_MAP.put("NEW_USER", new NewUserDiscount());
STRATEGY_MAP.put("MEMBER", new MemberDiscount());
STRATEGY_MAP.put("OLD_USER", new OldUserDiscount());
STRATEGY_MAP.put("FESTIVAL", new FestivalDiscount());
}
// 私有构造器,防止实例化
private DiscountStrategyFactory() {}
// 根据策略类型获取策略
public static DiscountStrategy getStrategy(String strategyType) {
DiscountStrategy strategy = STRATEGY_MAP.get(strategyType);
if (strategy == null) {
throw new IllegalArgumentException("未知的折扣策略类型:" + strategyType);
}
return strategy;
}
// 新增策略(扩展方法,无需修改原有逻辑)
public static void addStrategy(String strategyType, DiscountStrategy strategy) {
STRATEGY_MAP.put(strategyType, strategy);
}
}
4. 上下文类(使用策略)
import java.math.BigDecimal;
/**
* 上下文类:订单上下文,持有策略引用,提供策略执行入口
*/
public class OrderContext {
// 持有抽象策略引用
private DiscountStrategy discountStrategy;
// 构造方法注入策略(也可通过setter动态切换)
public OrderContext(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 动态切换策略
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 执行策略:计算最终订单价格
public BigDecimal calculateFinalPrice(BigDecimal originalPrice) {
System.out.println("当前使用策略:" + discountStrategy.getStrategyName());
return discountStrategy.calculate(originalPrice);
}
}
5. 客户端测试
import java.math.BigDecimal;
/**
* 客户端:测试不同折扣策略的使用
*/
public class StrategyClient {
public static void main(String[] args) {
// 订单原价:200元
BigDecimal originalPrice = new BigDecimal("200");
// 1. 使用新用户策略
DiscountStrategy newUserStrategy = DiscountStrategyFactory.getStrategy("NEW_USER");
OrderContext order1 = new OrderContext(newUserStrategy);
BigDecimal finalPrice1 = order1.calculateFinalPrice(originalPrice);
System.out.println("新用户最终价格:" + finalPrice1 + "元
");
// 2. 切换为会员策略
DiscountStrategy memberStrategy = DiscountStrategyFactory.getStrategy("MEMBER");
order1.setDiscountStrategy(memberStrategy);
BigDecimal finalPrice2 = order1.calculateFinalPrice(originalPrice);
System.out.println("会员最终价格:" + finalPrice2 + "元
");
// 3. 使用节日策略
DiscountStrategy festivalStrategy = DiscountStrategyFactory.getStrategy("FESTIVAL");
OrderContext order2 = new OrderContext(festivalStrategy);
BigDecimal finalPrice3 = order2.calculateFinalPrice(originalPrice);
System.out.println("节日最终价格:" + finalPrice3 + "元
");
// 4. 新增策略:优惠券抵扣(无需修改原有代码)
DiscountStrategy couponStrategy = new CouponDiscount(new BigDecimal("20"));
DiscountStrategyFactory.addStrategy("COUPON", couponStrategy);
DiscountStrategy newStrategy = DiscountStrategyFactory.getStrategy("COUPON");
OrderContext order3 = new OrderContext(newStrategy);
BigDecimal finalPrice4 = order3.calculateFinalPrice(originalPrice);
System.out.println("优惠券抵扣后价格:" + finalPrice4 + "元");
}
// 新增具体策略:优惠券抵扣(满100可用,抵扣固定金额)
static class CouponDiscount implements DiscountStrategy {
private BigDecimal couponAmount;
public CouponDiscount(BigDecimal couponAmount) {
this.couponAmount = couponAmount;
}
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
BigDecimal threshold = new BigDecimal("100");
if (originalPrice.compareTo(threshold) >= 0) {
return originalPrice.subtract(couponAmount);
}
return originalPrice;
}
@Override
public String getStrategyName() {
return "优惠券抵扣" + couponAmount + "元";
}
}
}
6. 输出结果
当前使用策略:新用户满100减30
新用户最终价格:170元
当前使用策略:会员9折
会员最终价格:180.00元
当前使用策略:节日满200减50+9.5折
节日最终价格:142.50元
当前使用策略:优惠券抵扣20元
优惠券抵扣后价格:180元

四、策略模式的经典应用(框架源码)

1. JDK中的Comparator接口
Comparator是抽象策略,不同的比较逻辑是具体策略,Collections.sort()是上下文,通过传入不同的Comparator实现动态切换排序算法:
List list = Arrays.asList("apple", "banana", "orange");
// 策略1:按长度升序
Collections.sort(list, Comparator.comparingInt(String::length));
// 策略2:按字母降序
Collections.sort(list, Comparator.reverseOrder());
2. Spring中的Resource加载策略
Spring的Resource接口定义了资源加载的抽象策略,ClassPathResource、FileSystemResource、UrlResource是具体策略,ResourceLoader是上下文,根据资源路径动态选择加载策略:
ResourceLoader loader = new DefaultResourceLoader();
// 策略1:加载类路径资源
Resource classPathResource = loader.getResource("classpath:application.yml");
// 策略2:加载文件系统资源
Resource fileResource = loader.getResource("file:/data/config.yml");
3. 支付框架中的支付策略(如PayJS、YeePay)
支付框架定义统一的PaymentStrategy接口,微信支付、支付宝支付、银联支付是具体策略,上下文类PaymentContext根据支付类型选择对应的策略,客户端无需关心支付细节。
五、模式优缺点与适用场景
1. 优点
- 消除条件判断:用策略工厂和上下文替代if-else/switch,代码更简洁;
- 扩展性好:新增算法只需新增策略类,无需修改原有代码,符合开闭原则;
- 算法解耦:每个算法封装为独立类,职责单一,便于维护和测试;
- 动态切换:客户端可通过上下文动态切换策略,适应不同业务场景。
2. 缺点
- 客户端需了解策略:客户端需知道所有策略的存在,才能选择合适的策略;
- 策略类数量增加:每个算法对应一个策略类,可能导致类数量增多;
- 上下文与策略耦合:上下文持有策略引用,若策略需依赖其他组件,可能增加配置复杂度。
3. 适用场景
- 多种算法可选:如排序、加密、压缩、支付方式、折扣规则;
- 消除条件判断:代码中存在大量if-else/switch判断不同逻辑;
- 算法需动态切换:如根据用户类型、环境配置选择不同策略;
- 扩展性要求高:需频繁新增或修改算法,且不希望影响原有代码。
4. 不适用场景
- 算法极少变化:仅1-2种算法,且后续不会扩展;
- 算法逻辑简单:无需封装为独立类(如简单的数值计算);
- 客户端需直接依赖算法细节:客户端需知道算法的内部实现。
六、与相关模式区别
| 模式名称 | 核心区别 | 适用场景 |
| 策略模式 | 封装可替换的算法,动态切换,基于接口组合 | 多种算法可选(如支付、排序) |
| 模板方法模式 | 父类定义流程骨架,子类实现步骤,基于继承 | 流程固定、步骤可变(如下单、审批) |
| 状态模式 | 封装对象状态,状态变化时自动切换行为,策略由状态决定 | 状态驱动的行为变化(如订单状态) |
| 工厂方法模式 | 封装对象创建,子类决定创建哪种对象,侧重对象创建 | 复杂对象创建(如不同类型产品) |
七、总结
- 策略模式的核心是“算法封装+动态选择”,通过抽象策略接口、具体策略类、上下文类和策略工厂,消除条件判断,提高扩展性;
- 关键设计:抽象策略定义统一接口,具体策略实现算法,策略工厂管理策略实例,上下文类持有策略并提供执行入口;
- 经典应用:JDK的
Comparator、Spring的ResourceLoader、支付框架的支付策略,核心是“算法解耦+动态切换”; - 实践建议:结合策略工厂(Map存储策略)彻底消除if-else;通过反射或配置文件初始化策略,符合开闭原则;策略类若无状态,可复用实例(享元模式优化)。









