策略模式实战:如何优雅替换if-else逻辑
1. 策略模式初探为什么我们需要它第一次接手老项目时我面对满屏的if-else地狱差点崩溃。订单处理逻辑里嵌套了17层条件判断每增加一个支付渠道就要修改核心业务类。这种经历让我深刻理解了策略模式的价值——它就像乐高积木把会变的部分拆成可插拔的组件。策略模式Strategy Pattern属于行为型设计模式其核心在于定义算法族将每个算法封装起来使它们可以互相替换。这种模式让算法的变化独立于使用算法的客户端在Java中通常通过接口实现类的方式呈现。关键认知策略模式不是简单的用接口替换if-else而是通过组合关系将行为委托给策略对象符合开闭原则对扩展开放对修改关闭2. 模式结构深度拆解2.1 UML核心三要素// 策略接口抽象策略 public interface DiscountStrategy { BigDecimal calculate(BigDecimal amount); } // 具体策略实现 public class VIPDiscount implements DiscountStrategy { Override public BigDecimal calculate(BigDecimal amount) { return amount.multiply(BigDecimal.valueOf(0.8)); } } // 上下文环境类 public class OrderService { private DiscountStrategy strategy; public void setStrategy(DiscountStrategy strategy) { this.strategy strategy; } public BigDecimal checkout(BigDecimal amount) { return strategy.calculate(amount); } }2.2 与状态模式的本质区别新手常混淆策略模式和状态模式二者UML相似但意图不同策略模式客户端主动选择算法策略状态模式状态转换由内部条件触发客户端无感知3. 实战电商促销系统改造3.1 传统if-else实现public BigDecimal applyDiscount(String userType, BigDecimal amount) { if (VIP.equals(userType)) { return amount.multiply(0.8); } else if (SVIP.equals(userType)) { return amount.multiply(0.7); } else { return amount; } }问题点每次新增用户类型都要修改方法违反开闭原则3.2 策略模式改造步骤定义策略接口public interface DiscountStrategy { boolean support(String userType); BigDecimal apply(BigDecimal amount); }实现具体策略public class VIPDiscount implements DiscountStrategy { Override public boolean support(String userType) { return VIP.equals(userType); } Override public BigDecimal apply(BigDecimal amount) { return amount.multiply(BigDecimal.valueOf(0.8)); } }创建策略工厂public class DiscountStrategyFactory { private static final ListDiscountStrategy STRATEGIES Arrays.asList( new VIPDiscount(), new SVIPDiscount(), new NewUserDiscount() ); public static DiscountStrategy getStrategy(String userType) { return STRATEGIES.stream() .filter(s - s.support(userType)) .findFirst() .orElseThrow(() - new IllegalArgumentException(未知用户类型)); } }客户端调用public BigDecimal checkout(String userType, BigDecimal amount) { DiscountStrategy strategy DiscountStrategyFactory.getStrategy(userType); return strategy.apply(amount); }4. 高级应用技巧4.1 Spring集成方案// 策略接口标注注解 StrategyType(discount) public interface DiscountStrategy { String getType(); // ... } // 自动注册策略 Component public class StrategyRegistry { private final MapString, DiscountStrategy strategyMap new ConcurrentHashMap(); Autowired public StrategyRegistry(ListDiscountStrategy strategies) { strategies.forEach(s - strategyMap.put(s.getType(), s)); } public DiscountStrategy getStrategy(String type) { return Optional.ofNullable(strategyMap.get(type)) .orElseThrow(() - new BusinessException(策略不存在)); } }4.2 组合策略模式处理需要多个策略协同的场景public class CompositeDiscountStrategy implements DiscountStrategy { private final ListDiscountStrategy strategies; public BigDecimal apply(BigDecimal amount) { BigDecimal result amount; for (DiscountStrategy strategy : strategies) { result strategy.apply(result); } return result; } }5. 性能优化与陷阱规避5.1 策略对象复用无状态策略可享元化public enum SingletonStrategy implements DiscountStrategy { INSTANCE; Override public BigDecimal apply(BigDecimal amount) { return amount.multiply(0.9); } }5.2 常见反模式策略类膨胀单个策略类超过500行代码应考虑拆分上下文污染避免在Context中保存策略无关的状态过度设计简单业务直接if-else更合适黄金法则当发现自己在复制粘贴条件分支逻辑时就是引入策略模式的最佳时机6. 测试策略模式6.1 单元测试要点class DiscountStrategyTest { Test void should_apply_20per_discount() { DiscountStrategy strategy new VIPDiscount(); BigDecimal result strategy.apply(new BigDecimal(100)); assertEquals(0, result.compareTo(new BigDecimal(80))); } Test void should_throw_when_strategy_not_found() { assertThrows(IllegalArgumentException.class, () - DiscountStrategyFactory.getStrategy(INVALID)); } }6.2 集成测试方案SpringBootTest class DiscountIntegrationTest { Autowired private StrategyRegistry registry; Test void should_apply_correct_discount() { DiscountStrategy strategy registry.getStrategy(VIP); // 验证策略行为 } }7. 模式演进与替代方案7.1 Java8函数式实现public class DiscountService { private final MapString, FunctionBigDecimal, BigDecimal strategies Map.of( VIP, amount - amount.multiply(0.8), SVIP, amount - amount.multiply(0.7) ); public BigDecimal apply(String type, BigDecimal amount) { return strategies.getOrDefault(type, a - a).apply(amount); } }7.2 规则引擎替代方案对于超复杂规则系统可考虑Drools等规则引擎KieSession kieSession kieContainer.newKieSession(); kieSession.insert(order); kieSession.fireAllRules();在电商秒杀系统中我们最终用策略模式管理了12种折扣策略和8种库存分配策略。当大促需要新增前100名5折策略时只需新增一个策略类核心流程完全不用修改。这种扩展性带来的开发效率提升在频繁活动的业务场景下尤为珍贵。