策略模式堪称设计模式入门必修课,多数开发者初次学习时,往往通过构造方法或set方法选择具体策略。这种做法本身没有错误,但在实际业务中,若能结合Spring的依赖注入能力,完全可以采用更优雅、更简洁的实现方式。
直接给出结论:将策略实现交由Spring容器管理的Map + Bean方式,能够显著降低上下文类的维护复杂度。
1、关于策略模式
经典策略模式的定义与基础实现可参考菜鸟教程等常见资料,这类教程通常基于构造方法实现,清晰易懂。然而本文重点探讨的是在此之上再抽象一层,借助Spring的IoC容器来动态调度策略。
2、使用Map + Bean方式实现策略模式
核心改动点在于:不再通过set方法或构造方法显式指定具体策略实现,而是由Spring容器将所有策略实现注入到一个Map中,随后根据业务传来的标识(例如枚举值)直接匹配并调用。
下面以电商系统中常见的“商品价格计算”场景为例,完整演示具体实现步骤。
2.1 定义顶层计算价格接口
首先定义一个价格计算策略接口,该接口作为所有具体策略实现的顶层规范:
/**
* 价格计算策略接口
*/
public interface PriceCalculatingStrategy {
/**
* 计算价格
*
* @param user 操作用户
* @param factor 影响因素
* @param prices 价格
* @return 计算后的价格
*/
BigDecimal calculatingProductPrices(User user, ProductPriceFactor factor, BigDecimal prices);
}
2.2 定义上下文
接下来是上下文类。核心要点是使用 @Resource 注入一个名为 priceStrategyMap 的Map,其中key为策略标识,value为对应的策略实现Bean。调用时,根据 factorType 从Map中取出目标策略并执行计算:
/**
* 价格计算上下文
*/
@Component
public class PricesContext {
@Resource(name = "priceStrategyMap")
private Map calculatingStrategyMap;
public BigDecimal calculating(User user, ProductPriceFactor factor, BigDecimal prices) {
PriceCalculatingStrategy priceCalculatingStrategy = calculatingStrategyMap.get(factor.getFactorType());
return priceCalculatingStrategy.calculatingProductPrices(user, factor, prices);
}
}
2.3 策略注入类(组件)
策略注入环节通过 @Configuration 配置类实现策略注册中心。将VIP和邮费两种策略的Bean注入后,统一放入HashMap,并暴露为Spring管理的Bean:
@Configuration
public class PriceStrategyConfig {
@Resource
private VIPPriceStrategy vipPriceStrategy;
@Resource
private PostageStrategy postageStrategy;
@Bean(name = "priceStrategyMap")
public Map submitOrderStrategyMap() {
Map strategyMap = new HashMap<>();
strategyMap.put(FactorType.VIP.factorType, vipPriceStrategy);
strategyMap.put(FactorType.POSTAGE.factorType, postageStrategy);
return strategyMap;
}
@Getter
enum FactorType {
VIP(3),
POSTAGE(2);
FactorType(int factorType) {
this.factorType = factorType;
}
private final int factorType;
}
}
2.4 策略模式的具体实现
有了顶层接口与注册中心后,业务逻辑实现在具体策略类中。例如VIP折扣策略执行打折逻辑,而邮费策略则在商品原价基础上增加邮费金额:
@Service
public class VIPPriceStrategy implements PriceCalculatingStrategy{
@Override
public BigDecimal calculatingProductPrices(User user, ProductPriceFactor factor, BigDecimal prices) {
// 这里忽略会员等级判断,用固定值测试
return user.getId().equals(1L) ? prices.multiply(new BigDecimal("0.7")) : prices.multiply(factor.getValue());
}
}
@Service(value = "postageStrategy")
public class PostageStrategy implements PriceCalculatingStrategy{
@Override
public BigDecimal calculatingProductPrices(User user, ProductPriceFactor factor, BigDecimal prices) {
// 忽略发货地、收货地之间的邮费计算,测试用固定值
return user.getId().equals(2L) ? prices.add(factor.getValue()) : prices;
}
}
2.5 策略接口调用
在业务调用层,直接面向 PricesContext 上下文,通过 factorType 匹配对应策略实现,完成价格计算:
@Service @RequiredArgsConstructor public class ProductServiceImpl extends ServiceImplimplements ProductService { private final ProductPriceFactorService productPriceFactorService; private final PricesContext pricesContext; public BigDecimal calculatingProductPrices(Long productId, Integer productNum, User user) { // 忽略参数校验 Product product = this.getById(productId); BigDecimal prices = product.getPrice().multiply(new BigDecimal(productNum)); prices = calculatingFactor(user, productId, prices); return prices; } private BigDecimal calculatingFactor(User user, Long productId, BigDecimal prices) { List allFactorByProDuct = productPriceFactorService.getAllFactorByProDuct(productId); if (CollectionUtils.isNotEmpty(allFactorByProDuct)) { for (ProductPriceFactor factor : allFactorByProDuct) { prices = pricesContext.calculating(user, factor, prices); } } return prices; } }
2.6 测试用例

2.7 额外
最后说明,本示例基于SpringBoot + MySQL + Maven + MyBatisPlus搭建,代码编写相对随意,重点在于展示Map+Bean思路的优越性。如有不足之处,欢迎指正,共同进步。
总结
总体而言,这种实现方案具备以下几方面优势:
- 策略注册与扩展只需在配置中心添加一个
put操作,无需修改上下文类; - 业务侧仅需传入标识类型,实现完全解耦;
- 借助Spring的IoC特性管理策略Bean生命周期,避免手动创建对象的繁琐。
希望该思路能为正在寻找更规范策略模式实现方案的开发者带来启发。
