1. 策略模式初探为什么我们需要它第一次接触策略模式时我正面临一个典型的业务场景电商平台的优惠计算系统。当时系统里充斥着各种if-else判断双十一要打折、会员日要满减、新用户要首单优惠...每次新增活动类型都像是在已经摇摇欲坠的代码堆上再摞一块砖。直到某天凌晨三点当我第N次修改calculateDiscount()方法时突然意识到——这不该是面向对象编程该有的样子。策略模式的核心思想其实很简单定义一系列算法将它们封装成独立的类并使它们可以相互替换。这种模式让算法的变化独立于使用它的客户端。听起来很抽象想象你是个餐厅老板收银台就是客户端而各种支付方式现金、信用卡、移动支付就是不同的策略。无论顾客选择哪种支付方式收银台的工作流程都不需要改变。关键理解策略模式不是用来解决有没有的问题而是解决多选一的问题。当你的系统中存在多种相似但又有差异的算法实现时就该考虑策略模式了。2. 策略模式的经典实现2.1 UML类图解析标准的策略模式包含三个角色Context环境类持有一个Strategy的引用负责与客户端交互Strategy抽象策略定义算法接口ConcreteStrategy具体策略实现具体算法用Java代码表示最基础的实现// 抽象策略接口 interface DiscountStrategy { double applyDiscount(double originalPrice); } // 具体策略类 class MemberDiscount implements DiscountStrategy { Override public double applyDiscount(double price) { return price * 0.9; // 会员9折 } } class NewUserDiscount implements DiscountStrategy { Override public double applyDiscount(double price) { return price * 0.8; // 新用户8折 } } // 环境类 class ShoppingCart { private DiscountStrategy strategy; public void setStrategy(DiscountStrategy strategy) { this.strategy strategy; } public double checkout(double total) { return strategy.applyDiscount(total); } }2.2 实际应用中的变体在实际开发中我们经常会根据需求调整经典实现。比如在Spring框架中结合依赖注入可以这样用Service public class DiscountService { private MapString, DiscountStrategy strategies; // 通过构造器自动注入所有策略实现 public DiscountService(ListDiscountStrategy strategyList) { this.strategies strategyList.stream() .collect(Collectors.toMap( s - s.getClass().getSimpleName(), Function.identity() )); } public double applyDiscount(String strategyName, double price) { return strategies.get(strategyName).applyDiscount(price); } }这种变体利用了Spring的自动装配特性将所有实现策略的Bean收集到Map中使用时通过名称调用避免了显式的setter方法。3. 策略模式在真实项目中的实战3.1 电商促销系统改造案例我曾参与过一个年GMV超10亿的电商平台重构原有的促销系统是这样的public BigDecimal calculateDiscount(User user, Order order) { if (user.isNewUser()) { return order.getTotal().multiply(BIG_DECIMAL_0_8); } else if (user.isVIP()) { return order.getTotal().multiply(BIG_DECIMAL_0_7); } else if (order.getCreateTime().isAfter(activityStartTime)) { return order.getTotal().subtract(BigDecimal.TEN); } // 更多if-else... }改造后的策略模式实现// 定义策略接口 public interface PromotionStrategy { boolean isApplicable(User user, Order order); BigDecimal applyDiscount(Order order); } // 具体策略实现 Component public class NewUserStrategy implements PromotionStrategy { Override public boolean isApplicable(User user, Order order) { return user.isNewUser(); } Override public BigDecimal applyDiscount(Order order) { return order.getTotal().multiply(BIG_DECIMAL_0_8); } } // 策略上下文 Service public class PromotionContext { Autowired private ListPromotionStrategy strategies; public BigDecimal execute(Order order) { return strategies.stream() .filter(s - s.isApplicable(order.getUser(), order)) .findFirst() .map(s - s.applyDiscount(order)) .orElse(order.getTotal()); } }改造后带来的直接收益新增促销类型只需添加新的Strategy实现类单元测试可以针对每个策略单独测试促销优先级调整只需修改filter逻辑代码行数减少40%可读性大幅提升3.2 性能优化策略对象的复用在初期实现中我们每次调用都创建新的策略对象后来通过对象池优化public class StrategyPool { private static final MapClass?, Object pool new ConcurrentHashMap(); SuppressWarnings(unchecked) public static T T getStrategy(ClassT strategyClass) { return (T)pool.computeIfAbsent(strategyClass, clazz - { try { return clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException(e); } }); } }对于无状态的策略对象即不包含成员变量可以放心复用实例减少GC压力。实测在高并发场景下QPS提升了约15%。4. 策略模式的高级应用与陷阱4.1 结合工厂模式动态选择策略在更复杂的场景中我们可能需要根据运行时条件动态选择策略。这时可以结合工厂模式public class StrategyFactory { public static DiscountStrategy createStrategy(Order order) { if (order.getItems().size() 5) { return new BulkDiscountStrategy(); } else if (order.getTotal().compareTo(BigDecimal.valueOf(1000)) 0) { return new HighValueDiscountStrategy(); } return new DefaultDiscountStrategy(); } }这种组合模式特别适合策略选择逻辑较复杂的场景但要注意避免工厂方法变得过于庞大。4.2 常见陷阱与规避方案陷阱1策略膨胀当策略类过多时超过20个维护会变得困难。解决方案按业务维度分组使用组合策略考虑改用规则引擎陷阱2上下文信息传递策略可能需要访问上下文数据两种处理方式// 方式1通过方法参数传递 interface Strategy { void execute(ContextData data); } // 方式2策略持有上下文引用 abstract class AbstractStrategy { protected Context context; public AbstractStrategy(Context context) { this.context context; } }陷阱3线程安全问题如果策略有状态需要特别注意// 错误示例有状态的策略 class CounterStrategy implements Strategy { private int count; // 非线程安全 public void execute() { count; } } // 正确做法使用ThreadLocal或每次新建实例 class SafeCounterStrategy implements Strategy { private final ThreadLocalInteger counter ThreadLocal.withInitial(() - 0); public void execute() { counter.set(counter.get() 1); } }4.3 策略模式与其他模式的对比与状态模式的异同相似都通过委托改变行为区别策略模式是主动选择状态模式是被动转换与模板方法模式的关系模板方法在父类定义骨架子类实现细节策略模式则是完全替换整个算法在实际项目中我经常发现新手会混淆这些模式。判断标准很简单如果变化的是完整算法用策略模式如果只是算法中的某些步骤变化用模板方法更合适。5. 现代编程语言中的策略模式演进5.1 Java 8的函数式实现自从Java 8引入lambda后策略模式可以更简洁地实现public class DiscountCalculator { private final MapString, FunctionDouble, Double strategies Map.of( VIP, price - price * 0.7, NEW, price - price * 0.8, DEFAULT, price - price ); public double calculate(String type, double price) { return strategies.getOrDefault(type, strategies.get(DEFAULT)) .apply(price); } }5.2 Spring中的策略模式最佳实践在Spring生态中策略模式常与这些特性结合使用Conditional实现条件化Bean注册通过ApplicationContext.getBeansOfType()获取所有策略实现结合Primary注解处理默认策略示例代码public interface PaymentStrategy { void pay(BigDecimal amount); } Service ConditionalOnProperty(name payment.alipay.enabled, havingValue true) class AlipayStrategy implements PaymentStrategy { public void pay(BigDecimal amount) { // 支付宝支付实现 } } Service Primary class DefaultPaymentStrategy implements PaymentStrategy { public void pay(BigDecimal amount) { // 默认支付方式 } }5.3 策略模式在微服务架构中的应用在微服务场景下策略模式演化为通过Feign Client实现不同服务调用策略使用Spring Cloud LoadBalancer定义路由策略结合配置中心实现动态策略切换典型架构客户端 → API网关 → [策略路由] → 服务A / 服务B / 服务C ↓ 策略配置中心这种架构下策略的切换可以通过配置中心实时生效无需重启服务。