1. 定义
策略模式(Strategy Pattern):定义一系列算法,把每一个算法封装起来,并且让它们可以互相替换。算法的变化不会影响到使用算法的客户端。它属于行为型设计模式。
核心思想:把可变的算法 / 行为抽离,和上下文解耦;面向接口,而不是面向实现。
一句话:同一个动作有多种不同实现,随时切换,用策略模式。
2. 角色组成
- Strategy(策略接口):统一抽象方法,所有具体策略实现这个接口
- ConcreteStrategy(具体策略):不同的算法、实现逻辑
- Context(上下文):持有策略对象的引用,对外提供调用入口,负责和客户端交互
3. 代码示例(订单支付场景,高频例子)
// 1. 策略接口
public interface PayStrategy {
void pay(Long orderId, BigDecimal money);
}
// 2. 具体策略:微信支付
public class WechatPayStrategy implements PayStrategy {
@Override
public void pay(Long orderId, BigDecimal money) {
System.out.println("微信支付,订单:" + orderId + ",金额:" + money);
}
}
// 支付宝支付
public class AlipayStrategy implements PayStrategy {
@Override
public void pay(Long orderId, BigDecimal money) {
System.out.println("支付宝支付,订单:" + orderId + ",金额:" + money);
}
}
// 银行卡支付
public class BankCardPayStrategy implements PayStrategy {
@Override
public void pay(Long orderId, BigDecimal money) {
System.out.println("银行卡支付");
}
}
// 3. 上下文 Context
public class PayContext {
private PayStrategy strategy;
// 注入策略
public PayContext(PayStrategy strategy) {
this.strategy = strategy;
}
// 动态切换策略
public void setStrategy(PayStrategy strategy) {
this.strategy = strategy;
}
public void executePay(Long orderId, BigDecimal money) {
strategy.pay(orderId, money);
}
}
// 客户端使用
public class Test {
public static void main(String[] args) {
PayContext context = new PayContext(new AlipayStrategy());
context.executePay(1001L, new BigDecimal("99.00"));
// 动态切换
context.setStrategy(new WechatPayStrategy());
context.executePay(1002L, new BigDecimal("199.00"));
}
}
4. 工厂 + 策略(工程里最常用!面试加分)
上面写法客户端还要自己 new 策略,业务里一般用策略工厂,根据类型编码获取策略,消除大量 if-else:
@Component
public class PayStrategyFactory {
private final Map<String, PayStrategy> strategyMap;
// Spring自动注入所有实现类
public PayStrategyFactory(List<PayStrategy> strategyList) {
strategyMap = new HashMap<>();
for (PayStrategy strategy : strategyList) {
if (strategy instanceof WechatPayStrategy) {
strategyMap.put("WECHAT", strategy);
} else if (strategy instanceof AlipayStrategy) {
strategyMap.put("ALIPAY", strategy);
}
}
}
public PayStrategy getStrategy(String payType) {
PayStrategy strategy = strategyMap.get(payType);
if (strategy == null) {
throw new RuntimeException("不支持的支付方式");
}
return strategy;
}
}
业务层直接根据 payType 从工厂拿策略,执行方法,完全消灭 if/else。
5. 适用场景
- 一个类有很多分支算法,大量
if-else / switch,后续还会新增分支(支付方式、运费计算、优惠规则、文件解析、排序算法)
- 需要动态切换算法
- 算法需要独立扩展、单独单元测试
常见例子:
- 订单优惠策略:满减、折扣、优惠券、直减
- 导出策略:Excel、PDF、CSV
- 消息推送:短信、APP 推送、钉钉、邮件
6. 优缺点
✅ 优点
- 符合开闭原则:新增策略只需要新增实现类,不用修改原有代码
- 消除大量 if-else,代码清爽,可读性高
- 算法独立,方便单独测试、复用
- 动态切换策略
❌ 缺点
- 策略多的时候,类会变多,类爆炸
- 客户端需要知道有哪些策略,才能选择(工厂模式可以缓解这个问题)
- 策略之间如果有公共逻辑,不方便复用(可以抽抽象基类)
7. 和其他模式对比(高频追问)
策略模式 vs 状态模式
- 策略:算法是平行、可互相替换,切换由客户端主动决定;策略之间无状态流转。比如支付方式,选微信还是支付宝,是主动选。
- 状态模式:对象内部状态自动流转,状态切换是被动的,由对象内部驱动。订单:待支付→已支付→已发货,状态自动流转。
策略模式 vs 模板方法
- 模板方法:固定流程骨架,部分步骤延迟子类实现(父类控制流程)
- 策略模式:整套算法整体替换,没有固定流程骨架
8. Spring 中的策略模式落地
- ResourceLoader,不同资源加载策略(文件、classpath、url)
- Comparator(JDK 自带策略接口)
- Converter 转换器
- 各种 Handler、Processor 很多都是策略思想
✅ 精简口述版(面试背诵)
策略模式是行为型模式,定义一套统一策略接口,不同的算法封装成独立策略实现类,上下文持有策略引用,支持动态替换。主要用来消除 if else,满足开闭原则;新增算法只新增策略类,不改老代码。工程上一般配合工厂模式使用,根据编码匹配对应的策略。缺点是策略多了会类膨胀。它和状态模式容易混淆:策略是主动选择平行算法,状态是对象内部自动流转状态。
更多设计模式面试梳理与 Java 进阶实战内容,欢迎在云栈社区一起沉淀和讨论。
|