一句话总结
策略模式定义一系列算法,把它们封装起来,使它们可以互相替换。核心:策略接口 + 具体策略实现 + 上下文。最大价值是消除 if-else,符合开闭原则。Spring 中最佳实践:利用 @Autowired Map<String, Strategy> 自动注入所有策略实现,通过 key 获取对应策略,新增策略只需加一个实现类,无需修改原有代码。
初级理解
反例:if-else 堆砌
# 糟糕的代码:if-else 堆砌
public BigDecimal calculatePrice(String type, BigDecimal price) {
if ("normal".equals(type)) {
return price; // 普通用户原价
} else if ("vip".equals(type)) {
return price.multiply(new BigDecimal("0.9")); // VIP 9折
} else if ("svip".equals(type)) {
return price.multiply(new BigDecimal("0.8")); // SVIP 8折
} else if ("new".equals(type)) {
return price.multiply(new BigDecimal("0.95")); // 新用户95折
}
return price;
}
# 问题:
# 1. 新增用户类型需修改方法(违反开闭原则)
# 2. if-else 越来越长,难以维护
# 3. 每个分支逻辑混在一起,职责不清
策略模式重构
# 1. 策略接口
public interface PriceStrategy {
BigDecimal calculate(BigDecimal price);
}
# 2. 具体策略
@Component("normal")
public class NormalStrategy implements PriceStrategy {
public BigDecimal calculate(BigDecimal price) { return price; }
}
@Component("vip")
public class VipStrategy implements PriceStrategy {
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.9"));
}
}
@Component("svip")
public class SvipStrategy implements PriceStrategy {
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.8"));
}
}
# 3. 上下文(自动注入所有策略)
@Component
public class PriceContext {
@Autowired
private Map<String, PriceStrategy> strategyMap;
public BigDecimal calculate(String type, BigDecimal price) {
PriceStrategy strategy = strategyMap.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的类型: " + type);
}
return strategy.calculate(price);
}
}
# 新增策略:只需加一个 @Component("new") 类
# 无需修改 PriceContext!符合开闭原则
中级深入
策略 + 工厂组合
# 策略模式负责"怎么算",工厂模式负责"选哪个"
# 两者结合,职责更清晰
# 工厂:负责策略选择
@Component
public class PriceStrategyFactory {
@Autowired
private Map<String, PriceStrategy> strategyMap;
public PriceStrategy getStrategy(String type) {
PriceStrategy strategy = strategyMap.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的类型: " + type);
}
return strategy;
}
}
# 上下文:负责调用策略
@Component
public class PriceService {
@Autowired
private PriceStrategyFactory factory;
public BigDecimal calculate(String type, BigDecimal price) {
PriceStrategy strategy = factory.getStrategy(type);
return strategy.calculate(price);
}
}
# 职责分离:
# Factory:策略选择(创建型)
# Strategy:算法封装(行为型)
# Service:业务编排
枚举策略
# 简单场景用枚举策略(无需 Spring 容器)
public enum PriceStrategyEnum {
NORMAL {
public BigDecimal calculate(BigDecimal price) { return price; }
},
VIP {
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.9"));
}
},
SVIP {
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.8"));
}
};
public abstract BigDecimal calculate(BigDecimal price);
}
# 使用
BigDecimal result = PriceStrategyEnum.valueOf("VIP")
.calculate(new BigDecimal("100"));
# 优点:简单、类型安全、天然单例
# 缺点:策略数量固定,新增需修改枚举(违反开闭原则)
# 适用:策略数量少且固定的场景
高级进阶
Spring 自动注入原理
# @Autowired Map<String, Strategy> 为什么能注入所有实现?
# Spring 在注入 Map 时:
# 1. 查找所有 Strategy 类型的 Bean
# 2. key = Bean 的名称(@Component("vip") → "vip")
# 3. value = Bean 实例
# 4. 组装成 Map 注入
# 等价于:
@Autowired
private List<PriceStrategy> strategyList; // 注入所有实现列表
# 也可以注入 List:
# Spring 会按 @Order 排序后注入所有实现
# 源码:DefaultListableBeanFactory.resolveDependency()
# 当注入类型是 Map<String, T> 时:
# 1. 查找所有 T 类型的 Bean
# 2. key = beanName, value = bean instance
# 3. 返回组装好的 Map
策略模式 vs 状态模式
| 对比维度 | 策略模式 | 状态模式 |
|---|---|---|
| 关注点 | 算法可替换 | 状态变化时行为改变 |
| 切换方式 | 外部主动选择 | 内部自动切换 |
| 客户端感知 | 知道不同策略 | 不知道状态变化 |
| 典型场景 | 支付方式、折扣计算 | 订单状态流转、审批流 |
实战场景
# 场景1:支付渠道策略
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
@Component("alipay")
public class AlipayStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) { /* 支付宝支付 */ }
}
@Component("wechat")
public class WechatStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) { /* 微信支付 */ }
}
# 场景2:消息通知策略(短信/邮件/App推送)
public interface NotifyStrategy {
void send(String userId, String message);
}
# 场景3:文件导出策略(Excel/PDF/CSV)
public interface ExportStrategy {
void export(List<?> data, OutputStream os);
}
# 场景4:Spring Resource 加载策略
# ClassPathResource / FileSystemResource / UrlResource
# 不同策略加载不同来源的资源
面试模拟
面试官:策略模式是什么?如何用策略模式消除 if-else?
你:策略模式定义算法族,分别封装,让它们可以互相替换。消除 if-else 的关键:定义策略接口 → 每个分支变成独立策略类 → 用 Map 存储策略(key=类型,value=策略实例)→ 通过 key 获取策略执行。Spring 中利用 @Autowired Map<String, Strategy> 自动注入所有策略实现,新增策略只需加类,无需改代码。
面试官:策略模式和工厂模式有什么区别?如何组合使用?
你:工厂模式关注"创建哪个对象"(创建型),策略模式关注"执行哪个算法"(行为型)。组合使用:工厂负责根据条件选择策略对象,策略负责执行具体算法。工厂解决"选哪个",策略解决"怎么做",职责分离更清晰。