1. 为什么我们需要策略模式
我第一次看到策略模式(Strategy Pattern)这个名词是在十年前的一个深夜。当时我正在维护一个电商平台的促销系统,代码里充斥着各种if-else和switch-case,每次新增促销类型都要修改原有代码,测试覆盖率直线下降。直到有一天,我翻开了《设计模式》这本书...
策略模式属于行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。这种模式让算法的变化独立于使用它的客户端。简单来说,就是把不同的算法或业务规则"装进盒子"里,需要哪个就取出哪个来用。
提示:策略模式特别适合处理那些在运行时需要根据不同条件选择不同算法或行为的场景。比如支付方式选择、促销规则计算、数据导出格式等。
2. 策略模式的核心结构
2.1 经典UML类图解析
策略模式的核心结构包含三个角色:
- Context(环境类):持有一个策略对象的引用,负责与客户端交互
- Strategy(抽象策略):定义所有支持的算法或行为的公共接口
- ConcreteStrategy(具体策略):实现Strategy接口的具体算法或行为
// 抽象策略接口 public interface DiscountStrategy { double applyDiscount(double originalPrice); } // 具体策略实现 public class NewCustomerDiscount implements DiscountStrategy { @Override public double applyDiscount(double originalPrice) { return originalPrice * 0.9; // 新用户9折 } } public class VIPDiscount implements DiscountStrategy { @Override public double applyDiscount(double originalPrice) { return originalPrice * 0.7; // VIP用户7折 } } // 环境类 public class Order { private DiscountStrategy strategy; public void setStrategy(DiscountStrategy strategy) { this.strategy = strategy; } public double calculatePrice(double originalPrice) { return strategy.applyDiscount(originalPrice); } }2.2 策略模式的变体实现
在实际开发中,策略模式可以有多种实现方式:
- 传统接口实现:如上例所示,最标准的实现方式
- 函数式接口+Lambda(Java8+):
// 定义函数式接口 @FunctionalInterface public interface DiscountStrategy { double apply(double originalPrice); } // 使用Lambda表达式 order.setStrategy(price -> price * 0.8);- 枚举策略:适用于策略数量固定且有限的场景
public enum DiscountType { NORMAL(price -> price), VIP(price -> price * 0.7), NEW_USER(price -> price * 0.9); private final DiscountStrategy strategy; DiscountType(DiscountStrategy strategy) { this.strategy = strategy; } public double apply(double price) { return strategy.apply(price); } }3. 策略模式的实战应用
3.1 电商促销系统改造
让我们看一个真实的电商促销系统改造案例。改造前的代码是这样的:
public double calculateDiscount(String userType, double price) { if ("VIP".equals(userType)) { return price * 0.7; } else if ("NEW_USER".equals(userType)) { return price * 0.9; } else if ("FESTIVAL".equals(userType)) { return price - 50; } // 更多if-else... return price; }使用策略模式重构后:
// 策略接口 public interface DiscountStrategy { boolean support(String userType); double apply(double price); } // 具体策略 public class VIPDiscount implements DiscountStrategy { @Override public boolean support(String userType) { return "VIP".equals(userType); } @Override public double apply(double price) { return price * 0.7; } } // 策略工厂 public class DiscountStrategyFactory { private static final List<DiscountStrategy> STRATEGIES = Arrays.asList( new VIPDiscount(), new NewUserDiscount(), new FestivalDiscount() ); public static DiscountStrategy getStrategy(String userType) { return STRATEGIES.stream() .filter(s -> s.support(userType)) .findFirst() .orElse(new DefaultDiscount()); } } // 使用方式 public double calculateDiscount(String userType, double price) { DiscountStrategy strategy = DiscountStrategyFactory.getStrategy(userType); return strategy.apply(price); }3.2 支付系统设计
另一个典型应用场景是支付系统。不同支付渠道(支付宝、微信、银联等)有不同的处理逻辑:
public interface PaymentStrategy { boolean pay(BigDecimal amount); String getType(); } public class AlipayStrategy implements PaymentStrategy { @Override public boolean pay(BigDecimal amount) { // 调用支付宝SDK } @Override public String getType() { return "ALIPAY"; } } public class PaymentContext { private PaymentStrategy strategy; public void setStrategy(PaymentStrategy strategy) { this.strategy = strategy; } public boolean executePayment(BigDecimal amount) { return strategy.pay(amount); } }4. 策略模式的进阶技巧
4.1 策略的自动发现与注册
当策略实现类很多时,可以结合Spring等框架实现自动注册:
@Service public class StrategyFactory { private final Map<String, DiscountStrategy> strategyMap = new HashMap<>(); @Autowired public StrategyFactory(List<DiscountStrategy> strategies) { strategies.forEach(strategy -> strategyMap.put(strategy.getType(), strategy) ); } public DiscountStrategy getStrategy(String type) { return strategyMap.get(type); } }4.2 策略与模板方法结合
策略模式常与模板方法模式结合使用:
public abstract class AbstractDiscountStrategy { // 模板方法 public final double calculate(double price) { validate(price); double discount = doCalculate(price); logDiscount(discount); return discount; } protected abstract double doCalculate(double price); private void validate(double price) { if (price < 0) throw new IllegalArgumentException(); } private void logDiscount(double discount) { // 记录日志 } }4.3 策略的上下文传递
有时策略需要访问上下文信息:
public interface ShippingStrategy { double calculate(ShippingContext context); } public class ShippingContext { private Address from; private Address to; private List<Item> items; // getters... } // 使用 context.setFrom(address1); context.setTo(address2); double cost = strategy.calculate(context);5. 策略模式的常见误区与解决方案
5.1 策略选择逻辑复杂化
问题:策略选择逻辑本身又变成了一堆if-else
解决方案:使用责任链模式或策略工厂来管理策略选择
public class StrategyChain { private List<DiscountStrategy> strategies; public DiscountStrategy getStrategy(String userType) { for (DiscountStrategy strategy : strategies) { if (strategy.support(userType)) { return strategy; } } return new DefaultStrategy(); } }5.2 策略类爆炸
问题:策略实现类过多,难以管理
解决方案:
- 使用组合而非继承
- 将简单策略合并
- 使用函数式接口减少类定义
// 使用Map存储简单策略 public class SimpleStrategyRegistry { private Map<String, Function<Double, Double>> strategies = new HashMap<>(); public void register(String type, Function<Double, Double> strategy) { strategies.put(type, strategy); } public double apply(String type, double price) { return strategies.getOrDefault(type, p -> p).apply(price); } }5.3 策略与状态混淆
问题:混淆策略模式与状态模式
区别:
- 策略模式:客户端主动选择算法
- 状态模式:状态转换由内部条件触发
6. 策略模式的性能考量
6.1 对象创建开销
每次使用策略都创建新对象可能有性能问题:
// 不好的做法:每次调用都新建策略对象 public double calculate(String type, double price) { DiscountStrategy strategy; if ("VIP".equals(type)) { strategy = new VIPDiscount(); } // ... return strategy.apply(price); } // 好的做法:重用策略对象 public class DiscountStrategies { private static final DiscountStrategy VIP = new VIPDiscount(); private static final DiscountStrategy NEW_USER = new NewUserDiscount(); public static DiscountStrategy get(String type) { if ("VIP".equals(type)) return VIP; // ... } }6.2 并发安全问题
确保策略实现是无状态的:
// 线程安全的策略 public class SafeStrategy implements DiscountStrategy { // 无成员变量 @Override public double apply(double price) { return price * 0.8; } } // 非线程安全的策略 public class UnsafeStrategy implements DiscountStrategy { private double rate; // 可变状态 public UnsafeStrategy(double rate) { this.rate = rate; } @Override public double apply(double price) { return price * rate; } }7. 策略模式在不同语言中的实现
7.1 JavaScript实现
// 策略对象 const strategies = { vip: (price) => price * 0.7, newUser: (price) => price * 0.9, normal: (price) => price }; // 上下文 function calculatePrice(strategyType, price) { return strategies[strategyType](price); }7.2 Python实现
from abc import ABC, abstractmethod class DiscountStrategy(ABC): @abstractmethod def apply(self, price: float) -> float: pass class VIPDiscount(DiscountStrategy): def apply(self, price: float) -> float: return price * 0.7 class Order: def __init__(self, strategy: DiscountStrategy): self._strategy = strategy def calculate_price(self, price: float) -> float: return self._strategy.apply(price)7.3 Go实现
package main type DiscountStrategy interface { Apply(price float64) float64 } type VIPDiscount struct{} func (v VIPDiscount) Apply(price float64) float64 { return price * 0.7 } type Order struct { strategy DiscountStrategy } func (o *Order) Calculate(price float64) float64 { return o.strategy.Apply(price) }8. 策略模式的测试策略
8.1 单元测试策略类
每个策略实现都应该有独立的测试:
public class VIPDiscountTest { @Test public void testApply() { DiscountStrategy strategy = new VIPDiscount(); assertEquals(70, strategy.apply(100), 0.001); } }8.2 集成测试上下文
测试上下文与策略的交互:
public class OrderTest { @Test public void testCalculateWithVIP() { Order order = new Order(); order.setStrategy(new VIPDiscount()); assertEquals(70, order.calculatePrice(100), 0.001); } }8.3 模拟策略测试
使用Mock对象测试上下文:
@Test public void testWithMockStrategy() { DiscountStrategy mockStrategy = mock(DiscountStrategy.class); when(mockStrategy.apply(100)).thenReturn(80.0); Order order = new Order(); order.setStrategy(mockStrategy); assertEquals(80, order.calculatePrice(100), 0.001); verify(mockStrategy).apply(100); }9. 策略模式与其他模式的关系
9.1 策略 vs 工厂模式
- 工厂模式:关注对象创建
- 策略模式:关注行为封装
常一起使用:工厂创建策略对象
9.2 策略 vs 命令模式
- 命令模式:封装请求为对象
- 策略模式:封装算法为对象
9.3 策略 vs 模板方法
- 模板方法:在父类定义算法骨架
- 策略模式:通过组合切换整个算法
10. 何时使用策略模式
10.1 适用场景
- 一个系统需要动态地在几种算法中选择一种
- 有多个条件语句的相同行为
- 不希望客户端知道复杂的、与算法相关的数据结构
- 算法可能被频繁替换或新增
10.2 不适用场景
- 算法很少变化
- 客户端必须知道策略的具体实现
- 策略对象与上下文之间有复杂的状态交互
11. 实际项目中的经验教训
- 策略命名:使用"策略"后缀(如PaymentStrategy)提高可读性
- 文档注释:每个策略类应该说明其适用场景
- 默认策略:总是提供一个合理的默认策略
- 策略发现:使用注解或配置减少硬编码
- 性能监控:关键策略添加性能监控
public class MonitoredStrategy implements DiscountStrategy { private final DiscountStrategy delegate; public MonitoredStrategy(DiscountStrategy delegate) { this.delegate = delegate; } @Override public double apply(double price) { long start = System.nanoTime(); try { return delegate.apply(price); } finally { Metrics.record("strategy." + delegate.getClass().getSimpleName(), System.nanoTime() - start); } } }12. 策略模式的演进与替代
12.1 函数式编程的替代
现代语言中,策略模式常被函数替代:
// 传统策略模式 order.setStrategy(new VIPDiscount()); // 函数式风格 order.setStrategy(price -> price * 0.7);12.2 规则引擎替代
复杂业务规则可考虑使用规则引擎(如Drools)替代策略模式:
KieSession kieSession = kieContainer.newKieSession(); kieSession.insert(order); kieSession.fireAllRules();12.3 策略模式的微服务化
在微服务架构中,策略可以独立为服务:
[客户端] -> [策略路由服务] -> [策略服务A] -> [策略服务B]13. 策略模式的反模式
- 策略膨胀:策略类过多,应考虑分层或组合
- 上下文过载:上下文传递太多参数,应重构策略接口
- 策略耦合:策略之间相互依赖,应解耦
- 过度设计:简单业务使用策略模式反而复杂化
14. 策略模式的最佳实践
- 保持策略纯净:策略应该只关注算法实现
- 接口设计稳定:策略接口一旦确定应尽量不变
- 使用组合:通过组合简单策略实现复杂逻辑
- 依赖注入:通过DI容器管理策略生命周期
- 明确文档:记录每个策略的适用场景和约束
15. 从if-else到策略模式的渐进式重构
15.1 第一步:识别重复条件
找出代码中重复的条件判断:
// 重构前 if (user.isVIP()) { price = price * 0.7; } if (order.isFestival()) { price = price - 50; }15.2 第二步:提取策略方法
将条件逻辑提取为策略方法:
// 提取策略方法 private double applyVIPDiscount(double price) { return price * 0.7; }15.3 第三步:引入策略接口
定义策略接口并实现:
public interface DiscountStrategy { double apply(double price); } public class VIPDiscount implements DiscountStrategy { public double apply(double price) { return price * 0.7; } }15.4 第四步:构建策略工厂
创建策略工厂管理策略:
public class DiscountStrategyFactory { public static DiscountStrategy getStrategy(User user) { if (user.isVIP()) return new VIPDiscount(); // ... } }15.5 第五步:替换条件逻辑
最终替换原有条件逻辑:
// 重构后 DiscountStrategy strategy = DiscountStrategyFactory.getStrategy(user); price = strategy.apply(price);16. 策略模式在框架中的应用
16.1 Spring中的策略模式
Spring广泛应用策略模式,如:
- ResourceLoader:不同资源加载策略
- PlatformTransactionManager:不同事务管理策略
- HandlerMapping:不同请求映射策略
16.2 JDK中的策略模式
Java标准库中的策略模式:
- Comparator:集合排序策略
- ThreadPoolExecutor:拒绝策略
- FileVisitor:文件遍历策略
17. 策略模式的扩展思考
17.1 策略的组合使用
多个策略可以组合使用:
public class CombinedDiscount implements DiscountStrategy { private List<DiscountStrategy> strategies; public double apply(double price) { double result = price; for (DiscountStrategy strategy : strategies) { result = strategy.apply(result); } return result; } }17.2 策略的优先级管理
有时需要处理策略优先级:
public class PriorityStrategy implements DiscountStrategy, Comparable<PriorityStrategy> { private int priority; private DiscountStrategy delegate; // ...实现方法... @Override public int compareTo(PriorityStrategy o) { return Integer.compare(priority, o.priority); } }17.3 策略的元数据管理
为策略添加元数据:
@StrategyMetadata( name = "VIP折扣", description = "VIP用户专享7折优惠", since = "1.0" ) public class VIPDiscount implements DiscountStrategy { // ... }18. 策略模式的性能优化
18.1 策略缓存
缓存策略计算结果:
public class CachedStrategy implements DiscountStrategy { private final DiscountStrategy delegate; private final Cache<Double, Double> cache; public double apply(double price) { return cache.get(price, () -> delegate.apply(price)); } }18.2 策略预热
提前初始化常用策略:
public class StrategyWarmer { public void warmUp() { DiscountStrategy strategy = DiscountStrategyFactory.getStrategy("VIP"); strategy.apply(100); // 触发类加载等初始化 } }18.3 策略懒加载
延迟加载不常用策略:
public class LazyStrategy implements DiscountStrategy { private Supplier<DiscountStrategy> supplier; private DiscountStrategy delegate; public double apply(double price) { if (delegate == null) { delegate = supplier.get(); } return delegate.apply(price); } }19. 策略模式的监控与运维
19.1 策略使用统计
记录策略使用情况:
public class InstrumentedStrategy implements DiscountStrategy { private final DiscountStrategy delegate; private final Counter counter; public double apply(double price) { counter.increment(); return delegate.apply(price); } }19.2 策略动态切换
运行时动态更换策略:
public class HotSwapStrategy implements DiscountStrategy { private volatile DiscountStrategy delegate; public void setDelegate(DiscountStrategy delegate) { this.delegate = delegate; } public double apply(double price) { return delegate.apply(price); } }19.3 策略健康检查
定期检查策略有效性:
public class HealthCheckedStrategy implements DiscountStrategy { private final DiscountStrategy delegate; public double apply(double price) { checkHealth(); return delegate.apply(price); } private void checkHealth() { // 验证策略是否仍然有效 } }20. 策略模式的未来演进
随着云原生和Serverless架构的兴起,策略模式也在演进:
- 策略即服务:将策略部署为独立微服务
- 动态策略:根据运行时指标自动调整策略
- AI策略:使用机器学习模型作为策略实现
// AI策略示例 public class AIDiscountStrategy implements DiscountStrategy { private final PredictionModel model; public double apply(double price) { return model.predict(price); } }在实际项目中,我从最初的if-else堆砌到现在的策略模式运用,最大的体会是:好的设计模式不是为了炫技,而是为了让代码更易于维护和扩展。当你的促销策略从5种增加到50种时,策略模式的价值就会真正显现出来。