策略模式精讲+实战
策略模式Strategy Pattern精讲 —— 结合 Spring 实战1. 官方标准定义GoF 原话定义一组算法将每个算法都封装起来并且使它们之间可以互相替换。该模式让算法的变化独立于使用算法的客户端调用方。核心原则针对接口编程而不是针对实现编程。策略模式Strategy Pattern是 GoF 23 种设计模式中的行为型模式。2. 一句话人话解读它是一个策略路由器。你只需要告诉上下文Context你想要什么策略标识比如ALIPAY它就会自动把对应的技能/算法比如支付宝支付逻辑拿出来执行而调用方完全不用关心底层是怎么if-else判断类型的。3. 模式的核心三要素结构定义为满足上面的定义模式必须包含以下 3 个角色角色职责对应代码抽象策略Strategy定义一个公共接口所有算法都遵循这个合同PaymentStrategy接口具体策略ConcreteStrategy实现接口的具体类封装了具体的业务算法AlipayStrategy、WechatStrategy上下文Context持有策略引用负责根据条件路由到具体策略PaymentContext设计原则封装变化找出应用中可能需要变化的部分把它们独立出来面向接口编程策略的具体实现依赖接口而不是依赖实现类组合优于继承Context 通过组合持有一个 Strategy 引用而不是继承具体策略4. 为什么这个定义很重要结合 Spring 场景互相替换因为 Spring 把策略类注入了MapString, Strategy只要type匹配随时可以替换某个策略的实现类调用方完全无感知。新增策略时客户端代码完全不用改完美符合开闭原则。变化独立于客户端客户端Controller只需要传WECHAT不需要知道微信支付类是否被 AOP 代理了。无论注入的是原始类还是代理类代码都能正常运行——这就是接口编程的威力。5. 代码实战解决支付场景假设有一个支付系统支持支付宝、微信等多种渠道。我们将使用 Spring 的依赖注入特性来优雅实现。5.1 原始反面案例使用instanceof// ❌ 反面教材每当新增支付方式都要修改此处代码违反开闭原则 public String pay(Object channel) { if (channel instanceof Alipay) { // 支付宝逻辑 } else if (channel instanceof WechatPay) { // 微信逻辑 } throw new UnsupportedOperationException(); }5.2 重构为策略模式优雅实现第一步定义策略接口public interface PaymentStrategy { // 策略标识用于前端传参替代 instanceof 判断 String getType(); // 执行支付 void pay(Double amount); }第二步实现具体策略交给 Spring 管理Component public class AlipayStrategy implements PaymentStrategy { Override public String getType() { return ALIPAY; } Override public void pay(Double amount) { System.out.println(使用支付宝支付 amount); // 此处调用支付宝 SDK... } } Component public class WechatStrategy implements PaymentStrategy { Override public String getType() { return WECHAT; } Override public void pay(Double amount) { System.out.println(使用微信支付 amount); // 此处调用微信 SDK... } }第三步创建策略上下文核心工厂利用 Spring 的构造函数注入将所有的策略List自动转换为MapString, Strategy。此时已彻底消灭instanceof。Component public class PaymentContext { private final MapString, PaymentStrategy strategyMap; Autowired public PaymentContext(ListPaymentStrategy strategies) { // 将 List 转为 MapKey 为策略自定义的类型标识 this.strategyMap strategies.stream() .collect(Collectors.toMap(PaymentStrategy::getType, Function.identity())); } // 对外统一调用接口 public void executePay(String type, Double amount) { PaymentStrategy strategy strategyMap.get(type); if (strategy null) { throw new IllegalArgumentException(不支持的支付方式: type); } strategy.pay(amount); } }第四步Controller 层调用RestController RequestMapping(/api/pay) public class PayController { Autowired private PaymentContext paymentContext; PostMapping public String pay(RequestParam String type, RequestParam Double amount) { // 前端传入 ALIPAY 或 WECHAT无需任何类型判断 paymentContext.executePay(type, amount); return 支付成功; } }6. 策略模式 vsinstanceof的优势对比维度if-elseinstanceof策略模式Map 路由开闭原则违反新增渠道需修改原有类符合新增渠道只需新建类加Component代码可读性随着分支增多逻辑臃肿难维护路由清晰业务逻辑内聚在各自类中测试难度需要 mock 大量无关分支可单独测试每个策略类运行时性能链式判断O(N) 复杂度Map 哈希查找O(1) 复杂度7. 联动避坑Spring 代理问题如果具体策略类如AlipayStrategy本身被Transactional或Cacheable注解Spring 会为其生成代理对象。此时直接通过new创建实例会失去代理能力必须通过 Spring 容器获取策略实例。这正是策略上下文Context通过依赖注入管理策略实例的重要原因。