当前位置: 首页 > news >正文

Java 设计模式——策略模式:从 3 种写法到 SpringBoot 进阶

Java 设计模式——策略模式:从 3 种写法到 SpringBoot 进阶

策略模式解决 “算法多样化且频繁切换” 的核心设计模式 —— 当一个业务存在多种实现逻辑(如支付方式、优惠规则),且需要根据条件动态选择时,策略模式能避免冗长的if-elseswitch判断,让代码更易扩展、更易维护。本文不仅拆解 3 种实战写法的代码细节,更会通过原理对比、SpringBoot 进阶场景、避坑指南,帮你彻底掌握 “如何用策略模式优化业务逻辑”,尤其是在复杂项目中的灵活应用。

文章目录

  • Java 设计模式——策略模式:从 3 种写法到 SpringBoot 进阶
    • 一、策略模式核心认知:看透 “解耦” 本质
      • 1. 传统`if-else`的致命问题
      • 2. 策略模式的 “解耦” 逻辑
    • 二、3 种实战写法:从基础到 SpringBoot 最优解
      • 写法 1:手动组装 Map(非 Spring 项目适用)
        • 核心思路
        • 完整代码实现
          • 1. 定义策略标识(枚举类)
          • 2. 定义抽象策略接口
          • 3. 实现具体策略类
          • 4. 策略持有类(手动注册与调度)
          • 5. 调用示例(业务入口)
        • 优缺点分析
        • 适用场景
      • 写法 2:Spring 接口注入(SpringBoot 快速开发)
        • 核心思路
        • 完整代码实现
          • 1. 优化策略标识(与 Bean 名称关联)
          • 2. 抽象策略接口(与写法 1 一致)
          • 3. 具体策略类(注册为 Spring Bean)
          • 4. 策略服务类(注入接口 Map)
          • 5. 调用示例(业务入口)
        • 优缺点分析
        • 适用场景
      • 写法 3:抽象类 + 工厂模式(SpringBoot 最优解)
        • 核心思路
        • 完整代码实现
          • 1. 策略标识枚举(保持元数据完整性)
          • 2. 抽象策略类(实现 Bean 生命周期接口)
          • 3. 具体策略类(继承抽象类并注册 Bean)
          • 4. 策略工厂类(管理策略注册与获取)
          • 5. 调用示例(业务入口,支持多场景)
        • 优缺点分析
        • 适用场景
    • 三、SpringBoot 进阶场景:策略模式的灵活扩展
      • 场景 1:策略动态开关(基于配置中心)
      • 场景 2:策略组合(多策略协同执行)
    • 四、避坑指南:策略模式实战中的 5 个关键问题
      • 1. 避免 “策略膨胀”:单一策略只做一件事
      • 2. 策略匹配失败:如何优雅处理 “未定义策略”?
      • 3. 策略线程安全:多线程下的共享资源问题
      • 4. 过度设计:不是所有`if-else`都需要策略模式
    • 五、总结:从 “能用” 到 “用好” 策略模式

一、策略模式核心认知:看透 “解耦” 本质

在深入代码之前,先明确策略模式的核心价值 —— 它不是 “为了用设计模式而用”,而是为了解决 “强耦合逻辑” 的痛点。

1. 传统if-else的致命问题

以 “订单支付” 场景为例,传统写法会将 “选择支付方式” 与 “执行支付逻辑” 硬编码在一起:

public String processPay(String payType, String orderNo, BigDecimal amount) {if ("ALIPAY".equals(payType)) {// 支付宝签名、接口调用、结果解析return alipayClient.execute(new AlipayTradePagePayRequest(orderNo, amount));} else if ("WECHAT_PAY".equals(payType)) {// 微信参数封装、证书验证、请求发送return wechatPayService.createOrder(orderNo, amount);} else if ("UNION_PAY".equals(payType)) {// 银联加密、网关请求、同步回调处理return unionPayGateway.pay(orderNo, amount);} else {throw new IllegalArgumentException("不支持的支付类型:" + payType);}
}

这种写法的问题会随着业务扩展逐渐暴露:

  • 违反开闭原则:新增 “京东支付” 时,必须修改processPay方法,可能误改原有逻辑;

  • 代码臃肿:当支付方式超过 5 种,方法会突破数百行,可读性急剧下降;

  • 职责混乱:一个方法同时负责 “判断逻辑” 和 “业务执行”,不符合单一职责原则;

  • 测试困难:修改某一种支付逻辑后,需重新测试所有支付方式,避免连锁影响。

2. 策略模式的 “解耦” 逻辑

策略模式通过 “抽象定义 - 具体实现 - 工厂调度” 三层结构,将耦合的逻辑拆分:

  • 抽象策略层:定义统一接口(如PayStrategy),约束所有支付方式的行为;

  • 具体策略层:每个支付方式封装为独立类(如AlipayStrategy),只负责自身业务;

  • 策略工厂层:管理所有策略,根据条件动态返回具体实现,隔绝 “选择” 与 “执行”。

最终实现 “新增策略不改旧代码、修改策略不影响其他逻辑” 的理想状态。

二、3 种实战写法:从基础到 SpringBoot 最优解

根据项目是否使用 Spring 框架,策略模式有 3 种主流实现方式,适用场景和复杂度各不相同。

写法 1:手动组装 Map(非 Spring 项目适用)

核心思路

不依赖任何框架,通过Map存储 “策略标识 - 具体策略” 的映射关系,手动注册策略,调用时根据标识从Map中获取并执行。

完整代码实现
1. 定义策略标识(枚举类)
package com.example.strategy.constant;import lombok.Getter;/*** 支付类型枚举:作为策略的唯一标识*/
@Getter
public enum PayTypeEnum {ALIPAY("支付宝", "alipay"),WECHAT_PAY("微信支付", "wechat_pay"),UNION_PAY("银联支付", "union_pay");// 策略描述:用于日志和前端展示private final String desc;// 策略标识:用于Map的key,与前端传参对应private final String code;PayTypeEnum(String desc, String code) {this.desc = desc;this.code = code;}// 静态方法:根据code获取枚举,避免调用方手动遍历public static PayTypeEnum getByCode(String code) {for (PayTypeEnum type : values()) {if (type.getCode().equals(code)) {return type;}}throw new IllegalArgumentException("无效的支付类型编码:" + code);}
}
2. 定义抽象策略接口
package com.example.strategy.strategy;import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;/*** 支付策略抽象接口:约束所有支付方式的行为*/
public interface PayStrategy {/*** 执行支付* @param requestDTO 支付请求参数(订单号、金额、用户ID等)* @return 支付响应结果(流水号、支付状态、跳转地址等)*/PayResponseDTO execute(PayRequestDTO requestDTO);/*** 获取当前策略对应的支付类型* @return 支付类型枚举*/PayTypeEnum getPayType();
}
3. 实现具体策略类
// 支付宝支付策略
package com.example.strategy.strategy.impl;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.PayStrategy;public class AlipayStrategy implements PayStrategy {@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {// 1. 封装支付宝请求参数(实际场景需按支付宝SDK规范处理)String alipayParam = String.format("out_trade_no=%s&total_amount=%s&subject=订单支付", requestDTO.getOrderNo(), requestDTO.getAmount());// 2. 调用支付宝接口(此处简化,实际需处理签名、超时等)String tradeNo = "ALIPAY_" + System.currentTimeMillis();// 3. 封装响应结果return PayResponseDTO.builder().orderNo(requestDTO.getOrderNo()).tradeNo(tradeNo).payStatus("SUCCESS").payType(PayTypeEnum.ALIPAY.getCode()).jumpUrl("https://openapi.alipay.com/gateway.do?" + alipayParam).build();}@Overridepublic PayTypeEnum getPayType() {return PayTypeEnum.ALIPAY;}
}// 微信支付策略(结构与支付宝一致,逻辑差异化)
package com.example.strategy.strategy.impl;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.PayStrategy;public class WechatPayStrategy implements PayStrategy {@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {// 微信支付逻辑:参数封装、证书验证、接口调用(此处简化)String tradeNo = "WECHAT_" + System.currentTimeMillis();return PayResponseDTO.builder().orderNo(requestDTO.getOrderNo()).tradeNo(tradeNo).payStatus("SUCCESS").payType(PayTypeEnum.WECHAT_PAY.getCode()).jumpUrl("weixin://wxpay/bizpayurl?out_trade_no=" + requestDTO.getOrderNo()).build();}@Overridepublic PayTypeEnum getPayType() {return PayTypeEnum.WECHAT_PAY;}
}
4. 策略持有类(手动注册与调度)
package com.example.strategy.holder;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.PayStrategy;
import com.example.strategy.strategy.impl.AlipayStrategy;
import com.example.strategy.strategy.impl.WechatPayStrategy;
import com.example.strategy.strategy.impl.UnionPayStrategy;
import java.util.HashMap;
import java.util.Map;/*** 策略持有类:管理所有策略,提供统一执行入口*/
public class PayStrategyHolder {// 存储策略的Map:key=支付类型枚举,value=具体策略,线程安全需手动保证private static final Map<PayTypeEnum, PayStrategy> STRATEGY_MAP = new HashMap<>(8);// 静态代码块:项目启动时手动注册所有策略static {STRATEGY_MAP.put(PayTypeEnum.ALIPAY, new AlipayStrategy());STRATEGY_MAP.put(PayTypeEnum.WECHAT_PAY, new WechatPayStrategy());STRATEGY_MAP.put(PayTypeEnum.UNION_PAY, new UnionPayStrategy());}/*** 根据支付类型执行策略* @param payType 支付类型枚举* @param requestDTO 支付请求参数* @return 支付响应结果*/public static PayResponseDTO processPay(PayTypeEnum payType, PayRequestDTO requestDTO) {// 1. 校验策略是否存在PayStrategy strategy = STRATEGY_MAP.get(payType);if (strategy == null) {throw new UnsupportedOperationException("暂不支持该支付方式:" + payType.getDesc());}// 2. 执行策略并返回结果return strategy.execute(requestDTO);}
}
5. 调用示例(业务入口)
package com.example.strategy.controller;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.holder.PayStrategyHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 支付接口控制器:业务入口,不关心具体支付逻辑*/
@RestController
@RequestMapping("/api/pay")
public class PayController {@PostMapping("/process")public PayResponseDTO processPay(@RequestBody PayRequestDTO requestDTO) {// 1. 解析支付类型(前端传code,转换为枚举)PayTypeEnum payType = PayTypeEnum.getByCode(requestDTO.getPayTypeCode());// 2. 调用策略持有类执行支付,无需关心具体实现return PayStrategyHolder.processPay(payType, requestDTO);}
}
优缺点分析
优点缺点
不依赖任何框架,适用于非 Spring 项目新增策略需修改STRATEGY_MAP,违反开闭原则
逻辑直观,便于理解策略模式底层原理线程安全需手动处理(如改用ConcurrentHashMap
无额外依赖,部署简单策略类初始化时机不可控,若依赖外部资源(如配置)易出问题
适用场景
  • 非 Spring 项目(如传统 SSM 或原生 Java 项目);

  • 策略数量固定、改动少的简单场景(如内部工具类);

  • 学习策略模式原理的入门案例。

写法 2:Spring 接口注入(SpringBoot 快速开发)

核心思路

利用 Spring 的 “接口多实现自动注入” 特性:所有策略类标注@Component注册为 Bean,Spring 会自动将同一接口的所有实现类注入到Map<String, 接口>中(key 为 Bean 名称,value 为具体实现),调用时根据 Bean 名称匹配策略。

完整代码实现
1. 优化策略标识(与 Bean 名称关联)
package com.example.strategy.constant;import lombok.Getter;@Getter
public enum PayTypeEnum {ALIPAY("支付宝", "alipayStrategy"), // Bean名称:alipayStrategyWECHAT_PAY("微信支付", "wechatPayStrategy"), // Bean名称:wechatPayStrategyUNION_PAY("银联支付", "unionPayStrategy"); // Bean名称:unionPayStrategyprivate final String desc;private final String beanName; // 与策略类的@Component名称对应PayTypeEnum(String desc, String beanName) {this.desc = desc;this.beanName = beanName;}
}
2. 抽象策略接口(与写法 1 一致)
package com.example.strategy.strategy;import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;public interface PayStrategy {PayResponseDTO execute(PayRequestDTO requestDTO);
}
3. 具体策略类(注册为 Spring Bean)
// 支付宝支付策略package com.example.strategy.strategy.impl;import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.PayStrategy;
import org.springframework.stereotype.Component;/*** 标注@Component,名称与PayTypeEnum的beanName一致*/
@Component("alipayStrategy")
public class AlipayStrategy implements PayStrategy {@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {// 支付宝支付逻辑(与写法1一致,此处简化)String tradeNo = "ALIPAY_" + System.currentTimeMillis();return PayResponseDTO.builder().orderNo(requestDTO.getOrderNo()).tradeNo(tradeNo).payStatus("SUCCESS").build();}
}// 微信支付策略
@Component("wechatPayStrategy")
public class WechatPayStrategy implements PayStrategy {@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {// 微信支付逻辑(此处简化)String tradeNo = "WECHAT_" + System.currentTimeMillis();return PayResponseDTO.builder().orderNo(requestDTO.getOrderNo()).tradeNo(tradeNo).payStatus("SUCCESS").build();}
}
4. 策略服务类(注入接口 Map)
package com.example.strategy.service;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.PayStrategy;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;/*** 策略服务类:通过@Resource注入所有PayStrategy实现*/
@Service
public class PayStrategyService {/*** Spring自动注入:key=策略类的@Component名称,value=具体策略实现*/@Resourceprivate Map<String, PayStrategy> payStrategyMap;/*** 执行支付策略* @param payType 支付类型枚举* @param requestDTO 支付请求参数* @return 支付响应结果*/public PayResponseDTO processPay(PayTypeEnum payType, PayRequestDTO requestDTO) {// 1. 根据枚举的beanName从Map中获取策略PayStrategy strategy = payStrategyMap.get(payType.getBeanName());if (strategy == null) {throw new UnsupportedOperationException("暂不支持该支付方式:" + payType.getDesc());}// 2. 执行策略return strategy.execute(requestDTO);}
}
5. 调用示例(业务入口)
package com.example.strategy.controller;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.service.PayStrategyService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;@RestController
@RequestMapping("/api/pay")
public class PayController {@Resourceprivate PayStrategyService payStrategyService;@PostMapping("/process")public PayResponseDTO processPay(@RequestBody PayRequestDTO requestDTO) {PayTypeEnum payType = PayTypeEnum.getByCode(requestDTO.getPayTypeCode());return payStrategyService.processPay(payType, requestDTO);}
}
优缺点分析
优点缺点
无需手动注册策略,Spring 自动管理 Bean 生命周期策略与 Bean 名称强绑定,名称写错会导致策略匹配失败
新增策略只需加类并标注@Component,符合开闭原则无法直接获取策略的元数据(如支付类型描述),需手动关联枚举
天然支持线程安全(Spring Bean 默认单例,且Map为 Spring 内部维护的不可变集合)若策略数量多,payStrategyMap的 key 管理成本高
适用场景
  • SpringBoot 项目的快速开发场景;

  • 策略数量适中(10 种以内)、元数据需求低的业务;

  • 团队对 Spring 注解使用熟练,追求开发效率的场景。

写法 3:抽象类 + 工厂模式(SpringBoot 最优解)

核心思路

结合 Spring Bean 生命周期工厂模式:定义抽象策略类并实现 InitializingBean 接口,所有具体策略类继承抽象类;当 Spring 初始化 Bean 时,抽象类的 afterPropertiesSet() 方法自动将策略注册到工厂;调用时通过工厂根据策略标识直接获取具体实现,彻底解耦策略注册与使用。

这种写法解决了 “写法 2 中策略与 Bean 名称强绑定” 的问题,且支持策略元数据管理,是中大型 SpringBoot 项目的首选方案。

完整代码实现
1. 策略标识枚举(保持元数据完整性)
package com.example.strategy.constant;import lombok.Getter;@Getter
public enum PayTypeEnum {ALIPAY("支付宝", "alipay", 1),    // 1:优先级(数值越小优先级越高)WECHAT_PAY("微信支付", "wechat", 2),UNION_PAY("银联支付", "union", 3);private final String desc;       // 策略描述private final String code;       // 前端传参标识private final int priority;      // 策略优先级(用于多策略匹配时排序)PayTypeEnum(String desc, String code, int priority) {this.desc = desc;this.code = code;this.priority = priority;}// 根据code获取枚举public static PayTypeEnum getByCode(String code) {for (PayTypeEnum type : values()) {if (type.getCode().equals(code)) {return type;}}throw new IllegalArgumentException("无效的支付类型编码:" + code);}
}
2. 抽象策略类(实现 Bean 生命周期接口)
package com.example.strategy.strategy;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.factory.PayStrategyFactory;
import org.springframework.beans.factory.InitializingBean;/*** 抽象策略类:所有支付策略的父类,统一管理策略注册*/
public abstract class AbstractPayStrategy implements InitializingBean {/*** 核心方法:执行支付(由子类实现具体逻辑)*/public abstract PayResponseDTO execute(PayRequestDTO requestDTO);/*** 获取当前策略对应的支付类型(由子类绑定具体枚举)*/public abstract PayTypeEnum getPayType();/*** Spring Bean 初始化完成后,自动注册到策略工厂* (InitializingBean 接口的方法,Bean 初始化时触发)*/@Overridepublic void afterPropertiesSet() throws Exception {PayStrategyFactory.register(this);System.out.printf("支付策略注册成功:%s(编码:%s)%n", getPayType().getDesc(), getPayType().getCode());}
}
3. 具体策略类(继承抽象类并注册 Bean)
// 支付宝支付策略
package com.example.strategy.strategy.impl;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.AbstractPayStrategy;
import org.springframework.stereotype.Component;/*** 具体策略类:只需标注@Component,无需指定名称,由抽象类自动注册*/
@Component
public class AlipayStrategy extends AbstractPayStrategy {@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {// 1. 业务校验(如订单状态、金额合法性)validateRequest(requestDTO);// 2. 调用支付宝SDK执行支付(此处简化,实际需处理签名、超时重试)String tradeNo = "ALIPAY_" + System.currentTimeMillis();System.out.printf("支付宝支付执行成功:订单号[%s],流水号[%s]%n", requestDTO.getOrderNo(), tradeNo);// 3. 封装响应结果return PayResponseDTO.builder().orderNo(requestDTO.getOrderNo()).tradeNo(tradeNo).payStatus("PAY_SUCCESS").payTypeDesc(getPayType().getDesc()).jumpUrl("https://openapi.alipay.com/gateway.do?out_trade_no=" + requestDTO.getOrderNo()).build();}/*** 绑定当前策略对应的支付类型枚举*/@Overridepublic PayTypeEnum getPayType() {return PayTypeEnum.ALIPAY;}/*** 自定义业务校验方法(策略内聚自身校验逻辑)*/private void validateRequest(PayRequestDTO requestDTO) {if (requestDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {throw new IllegalArgumentException("支付金额必须大于0:" + requestDTO.getAmount());}if (requestDTO.getOrderNo() == null || requestDTO.getOrderNo().length() > 32) {throw new IllegalArgumentException("订单号无效:" + requestDTO.getOrderNo());}}
}// 微信支付策略package com.example.strategy.strategy.impl;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.strategy.AbstractPayStrategy;
import org.springframework.stereotype.Component;@Component
public class WechatPayStrategy extends AbstractPayStrategy {@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {// 微信支付逻辑(类似支付宝,包含校验、SDK调用、结果封装)String tradeNo = "WECHAT_" + System.currentTimeMillis();System.out.printf("微信支付执行成功:订单号[%s],流水号[%s]%n", requestDTO.getOrderNo(), tradeNo);return PayResponseDTO.builder().orderNo(requestDTO.getOrderNo()).tradeNo(tradeNo).payStatus("PAY_SUCCESS").payTypeDesc(getPayType().getDesc()).jumpUrl("weixin://wxpay/bizpayurl?out_trade_no=" + requestDTO.getOrderNo()).build();}@Overridepublic PayTypeEnum getPayType() {return PayTypeEnum.WECHAT_PAY;}
}
4. 策略工厂类(管理策略注册与获取)
package com.example.strategy.factory;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.strategy.AbstractPayStrategy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;/*** 策略工厂:单例模式,管理所有策略的注册、存储、获取*/
public class PayStrategyFactory {// 线程安全的Map:存储策略,key=支付类型枚举,value=具体策略private static final Map<PayTypeEnum, AbstractPayStrategy> STRATEGY_MAP = new ConcurrentHashMap<>(8);/*** 注册策略(由AbstractPayStrategy的afterPropertiesSet()调用)*/public static void register(AbstractPayStrategy strategy) {PayTypeEnum payType = strategy.getPayType();if (STRATEGY_MAP.containsKey(payType)) {throw new IllegalStateException("支付策略已存在,避免重复注册:" + payType.getDesc());}STRATEGY_MAP.put(payType, strategy);}/*** 根据支付类型枚举获取策略(最常用方法)*/public static AbstractPayStrategy getStrategy(PayTypeEnum payType) {AbstractPayStrategy strategy = STRATEGY_MAP.get(payType);if (strategy == null) {throw new UnsupportedOperationException("暂不支持该支付方式:" + payType.getDesc());}return strategy;}/*** 根据支付类型编码获取策略(适配前端传参)*/public static AbstractPayStrategy getStrategyByCode(String code) {PayTypeEnum payType = PayTypeEnum.getByCode(code);return getStrategy(payType);}/*** 获取所有策略(支持批量操作,如策略状态查询)*/public static Map<PayTypeEnum, AbstractPayStrategy> getAllStrategies() {return new ConcurrentHashMap<>(STRATEGY_MAP); // 返回不可变副本,避免外部修改}/*** 根据优先级排序获取策略(适用于多策略匹配场景)*/public static Map<PayTypeEnum, AbstractPayStrategy> getStrategiesByPriority() {return STRATEGY_MAP.entrySet().stream().sorted((entry1, entry2) -> Integer.compare(entry1.getKey().getPriority(), entry2.getKey().getPriority())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1, ConcurrentHashMap::new));}
}
5. 调用示例(业务入口,支持多场景)
package com.example.strategy.controller;import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.factory.PayStrategyFactory;
import com.example.strategy.strategy.AbstractPayStrategy;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;@RestController
@RequestMapping("/api/pay")
public class PayController {/*** 场景1:根据前端传的支付编码执行支付*/@PostMapping("/processByCode")public PayResponseDTO processByCode(@RequestBody PayRequestDTO requestDTO) {// 1. 根据编码获取策略AbstractPayStrategy strategy = PayStrategyFactory.getStrategyByCode(requestDTO.getPayTypeCode());// 2. 执行策略return strategy.execute(requestDTO);}/*** 场景2:根据枚举直接执行支付(内部服务调用场景)*/@PostMapping("/processByEnum")public PayResponseDTO processByEnum(@RequestBody PayRequestDTO requestDTO) {// 内部服务可直接传入枚举,跳过编码解析AbstractPayStrategy strategy = PayStrategyFactory.getStrategy(requestDTO.getPayType());return strategy.execute(requestDTO);}/*** 场景3:获取所有策略(如前端下拉框加载支付方式列表)*/@PostMapping("/listAll")public Map<String, String> listAllStrategies() {return PayStrategyFactory.getAllStrategies().entrySet().stream().collect(Collectors.toMap(entry -> entry.getKey().getCode(), entry -> entry.getKey().getDesc()));}
}
优缺点分析
优点缺点
新增策略只需加类(继承抽象类 + 标注@Component),完全符合开闭原则实现稍复杂,需理解 Spring Bean 生命周期(InitializingBean
策略与标识(枚举)显式绑定,元数据(描述、优先级)管理清晰抽象类强约束,所有策略必须继承同一父类,灵活性略低于接口
工厂类提供多场景获取方法(按编码、按枚举、按优先级),适配复杂业务若策略需实现多个接口,需额外处理多继承问题(可通过组合模式补充)
线程安全(ConcurrentHashMap),支持高并发场景-
适用场景
  • 中大型 SpringBoot 项目,策略数量多(10 种以上)且需频繁扩展;

  • 需管理策略元数据(如优先级、描述),或支持多场景策略获取;

  • 团队追求代码规范性,需统一策略注册与调用逻辑。

三、SpringBoot 进阶场景:策略模式的灵活扩展

掌握基础写法后,结合 SpringBoot 生态可实现更复杂的业务需求,以下是 3 个高频进阶场景。

场景 1:策略动态开关(基于配置中心)

需求:通过 Nacos/Apollo 配置中心动态启用 / 禁用某类支付策略(如活动期间临时关闭银联支付)。

实现思路:在抽象策略类中注入配置,注册时根据配置判断是否启用。

package com.example.strategy.strategy;import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.factory.PayStrategyFactory;
import org.springframework.beans.factory.InitializingBean;public abstract class AbstractPayStrategy implements InitializingBean {// 从Nacos获取配置:pay.strategy.{code}.enabled,默认启用@NacosValue(value = "${pay.strategy.${payType.code:}.enabled:true}", autoRefreshed = true)private boolean enabled;// 注入支付类型枚举(需通过子类传递)private PayTypeEnum payType;@Overridepublic void afterPropertiesSet() throws Exception {// 子类实现getPayType方法即可this.payType = getPayType();// 根据配置决定是否注册策略if (enabled) {PayStrategyFactory.register(this);System.out.printf("支付策略注册成功:%s(编码:%s)%n", payType.getDesc(), payType.getCode());} else {System.out.printf("支付策略已禁用:%s(编码:%s)%n", payType.getDesc(), payType.getCode());}}// 配置变更时重新判断策略状态(Nacos配置变更触发)@NacosValue(value = "${pay.strategy.${payType.code:}.enabled:true}", autoRefreshed = true)public void setEnabled(boolean enabled) {this.enabled = enabled;if (enabled && !PayStrategyFactory.getAllStrategies().containsKey(payType)) {PayStrategyFactory.register(this);System.out.printf("支付策略启用:%s(编码:%s)%n", payType.getDesc(), payType.getCode());} else if (!enabled) {// 从工厂中移除禁用的策略(需在工厂类新增remove方法)PayStrategyFactory.remove(payType);System.out.printf("支付策略禁用:%s(编码:%s)%n", payType.getDesc(), payType.getCode());}}// 其他抽象方法...
}

场景 2:策略组合(多策略协同执行)

需求:部分订单需同时执行 “支付” 与 “优惠券抵扣” 策略(如支付时自动使用用户的最高优先级优惠券)。

实现思路:通过 “组合策略” 封装多个子策略,统一调用。

package com.example.strategy.strategy.composite;import com.example.strategy.constant.PayTypeEnum;
import com.example.strategy.dto.PayRequestDTO;
import com.example.strategy.dto.PayResponseDTO;
import com.example.strategy.dto.CouponDTO;
import com.example.strategy.strategy.AbstractPayStrategy;
import com.example.strategy.strategy.CouponStrategy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;/*** 组合策略:同时执行支付策略与优惠券策略*/
@Component
public class PayWithCouponCompositeStrategy {// 注入优惠券策略工厂    @Resourceprivate CouponStrategy couponStrategy;/*** 执行组合策略:先抵扣优惠券,再执行支付*/public PayResponseDTO execute(PayRequestDTO requestDTO) {// 1. 计算优惠券抵扣金额CouponDTO coupon = couponStrategy.getBestCoupon(requestDTO.getUserId(), requestDTO.getAmount());BigDecimal actualPayAmount = requestDTO.getAmount().subtract(coupon.getDiscountAmount());requestDTO.setAmount(actualPayAmount); // 更新实际支付金额// 2. 执行支付策略AbstractPayStrategy payStrategy = PayStrategyFactory.getStrategyByCode(requestDTO.getPayTypeCode());PayResponseDTO responseDTO = payStrategy.execute(requestDTO);// 3. 记录优惠券使用记录couponStrategy.useCoupon(coupon.getCouponId(), requestDTO.getOrderNo());// 4. 补充优惠券信息到响应结果responseDTO.setCouponId(coupon.getCouponId());responseDTO.setDiscountAmount(coupon.getDiscountAmount());responseDTO.setActualPayAmount(actualPayAmount);return responseDTO;}
}

四、避坑指南:策略模式实战中的 5 个关键问题

1. 避免 “策略膨胀”:单一策略只做一件事

策略模式的灵活性可能导致策略类数量爆炸,尤其是当业务存在多级分支时(如 “支付宝 - 信用卡支付”“支付宝 - 余额支付”)。

解决方案

  • 用 “策略 + 模板方法” 组合:将公共逻辑抽离到抽象类(如支付前的签名逻辑),子类只实现差异化部分;

  • 复杂策略拆分为 “基础策略 + 装饰器”:如AlipayStrategy作为基础策略,AlipayCreditCardDecorator装饰器添加信用卡支付逻辑。

2. 策略匹配失败:如何优雅处理 “未定义策略”?

当传入的策略标识不存在时,直接抛异常可能影响用户体验,尤其在前端传参场景。

解决方案

  • 工厂类中定义 “默认策略”:对未匹配的标识,返回DefaultPayStrategy(如提示 “暂不支持该支付方式,请联系客服”);

  • 新增策略时同步更新枚举和前端文档,避免传参错误。

// 工厂类中添加默认策略
public static AbstractPayStrategy getStrategy(PayTypeEnum payType) {return STRATEGY_MAP.getOrDefault(payType, new DefaultPayStrategy(payType));
}// 默认策略实现
public class DefaultPayStrategy extends AbstractPayStrategy {private final PayTypeEnum payType;public DefaultPayStrategy(PayTypeEnum payType) {this.payType = payType;}@Overridepublic PayResponseDTO execute(PayRequestDTO requestDTO) {throw new BusinessException("PAY_TYPE_NOT_SUPPORT", "暂不支持" + payType.getDesc() + ",请选择其他支付方式");}@Overridepublic PayTypeEnum getPayType() {return payType;}
}

3. 策略线程安全:多线程下的共享资源问题

若策略类中包含共享变量(如计数器、缓存),多线程调用可能导致数据错乱。

解决方案

  • 策略类设计为无状态(不定义成员变量,或成员变量为final);

  • 若必须有状态,使用ThreadLocal隔离线程变量,或通过@Scope("prototype")将策略 Bean 设为原型模式(每次获取新实例)。

4. 过度设计:不是所有if-else都需要策略模式

当业务逻辑简单(如固定 2-3 种策略且长期不变),用策略模式反而会增加代码复杂度。

判断标准

  • 策略数量是否可能超过 3 种?

  • 未来 6 个月内是否有新增策略的计划?

  • 每种策略的代码量是否超过 10 行?

    若以上答案均为 “否”,直接用if-else更合适。

五、总结:从 “能用” 到 “用好” 策略模式

策略模式的核心不是代码模板,而是 **“分离变化与不变”** 的设计思想:

  • 不变的部分:业务动作的定义(如 “支付” 这个行为);

  • 变化的部分:业务动作的具体实现(如支付宝、微信的不同支付逻辑)。

3 种写法的选择建议:

写法适用场景维护成本推荐指数
手动组装 Map非 Spring 项目、简单场景高(需手动注册)★★☆☆☆
Spring 接口注入快速开发、策略数量少中(依赖 Bean 名称)★★★☆☆
抽象类 + 工厂模式中大型项目、频繁扩展低(自动注册 + 元数据管理)★★★★★

在实际开发中,策略模式常与其他模式结合使用:

  • 策略 + 工厂:解决策略创建问题(如本文实现);

  • 策略 + 模板方法:抽离策略间的公共逻辑;

  • 策略 + 装饰器:动态增强策略功能(如日志、缓存);

  • 策略 + 观察者:实现策略执行后的通知机制(如支付成功后发送消息)。

掌握这些组合技巧,才能在复杂业务中真正发挥策略模式的价值,写出 “易扩展、易维护、高内聚、低耦合” 的代码。


文章转载自:

http://7FNBaKed.dgpxp.cn
http://MeRIjPse.dgpxp.cn
http://YUSTugU3.dgpxp.cn
http://BHRxNHOr.dgpxp.cn
http://QQ8ctlaq.dgpxp.cn
http://CMjuvoLQ.dgpxp.cn
http://CnuusZPZ.dgpxp.cn
http://PmfxT0Sl.dgpxp.cn
http://dx246tnn.dgpxp.cn
http://bQw4RG4M.dgpxp.cn
http://cQVSbPtf.dgpxp.cn
http://ikrXmNGX.dgpxp.cn
http://Yxsv91UL.dgpxp.cn
http://h6bcZXoO.dgpxp.cn
http://SAu8Yztq.dgpxp.cn
http://PWQHMHZB.dgpxp.cn
http://ylGOAcyr.dgpxp.cn
http://1ShToUIK.dgpxp.cn
http://9Ohql2Gw.dgpxp.cn
http://hJKIAM8e.dgpxp.cn
http://ee5SuQ08.dgpxp.cn
http://O6WFhHsx.dgpxp.cn
http://FY7akLHR.dgpxp.cn
http://phCqiY7W.dgpxp.cn
http://lJ9BszfC.dgpxp.cn
http://7bArwcEr.dgpxp.cn
http://cioR4DEz.dgpxp.cn
http://uoulLCkJ.dgpxp.cn
http://zp4oyFd3.dgpxp.cn
http://bUrepo5Z.dgpxp.cn
http://www.dtcms.com/a/387294.html

相关文章:

  • JVM:性能调优的理解
  • AR眼镜在巡检业务中的软件架构设计|阿法龙XR云平台
  • 活动预告 | Paraverse × Unity:Unity云XR串流——突破设备与平台限制
  • 第十四届蓝桥杯青少组C++选拔赛[2022.12.18]第二部分编程题(5、猴子拿桃)
  • 二维码辅助回桩之二维码识别
  • Mojo vs Python vs Rust,2025年搞AI,怎么学
  • 从软件工程角度谈企业管理
  • 【C语言】C 语言自定义类型:联合与枚举的基础解析
  • 模型部署:(五)安卓端部署Yolov8关键点检测项目全流程记录
  • 在业务应用中集成 go-commons,实现应用+系统双指标监控
  • ESP32-C3四种工作模式
  • ReactNative中实现可拖拽的温度计组件
  • react snippets
  • 基于Matlab高低频混合建模的大气湍流相位屏生成算法
  • 2025年8月SCI-袋鼠逃生优化算法Kangaroo Escape Optimizer-附Matlab免费代码
  • Node.js 创建 TCP 服务
  • 关于鸿蒙配置HMRouter的问题,比如白屏等
  • 为什么 socket.io 客户端在浏览器能连接服务器但在 Node.js 中报错 transport close?
  • Express框架介绍(基于Node.js的轻量级、灵活的Web应用框架)
  • Lustre Ceph GlusterFS NAS 需要挂载在k8s容器上,数据量少,选择哪一个存储较好
  • Axios与Java Spring构建RESTful API服务集成指南
  • 贪心算法应用:集合覆盖问题详解
  • 分布式拜占庭容错算法——权益证明(PoS)算法详解
  • Maven 深入profiles和mirrors标签
  • SQL Server 运维实战指南:从问题排查到性能优化
  • FFmpeg的安装及简单使用
  • F019 vue+flask海外购商品推荐可视化分析系统一带一路【三种推荐算法】
  • R语言数据统计分析与ggplot2高级绘图实践应用
  • Java 设计模式——观察者模式进阶:分布式场景扩展与实战配置
  • ​​[硬件电路-238]:电阻、电容、电感对数字电路中的作用