策略模式?

2025年 阅读约 10 分钟 面试指南 · 设计模式

深入解析策略模式:消除if-else的利器、Spring中策略模式实践(Map注入)、策略+工厂组合、枚举策略,附面试模拟问答。

一句话总结

策略模式定义一系列算法,把它们封装起来,使它们可以互相替换。核心:策略接口 + 具体策略实现 + 上下文。最大价值是消除 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> 自动注入所有策略实现,新增策略只需加类,无需改代码。

面试官:策略模式和工厂模式有什么区别?如何组合使用?

你:工厂模式关注"创建哪个对象"(创建型),策略模式关注"执行哪个算法"(行为型)。组合使用:工厂负责根据条件选择策略对象,策略负责执行具体算法。工厂解决"选哪个",策略解决"怎么做",职责分离更清晰。