1. 策略模式:从业务痛点开始的思考
作为一名经历过多个电商系统开发的Java工程师,我至今记得第一次面对支付模块迭代时的痛苦。产品经理拿着新需求过来:"这次要加个Apple Pay,下个月还要对接银联云闪付...",而当时的代码是这样的:
java复制public void pay(String payType, BigDecimal amount) {
if ("wechat".equals(payType)) {
// 200行微信支付逻辑
} else if ("alipay".equals(payType)) {
// 150行支付宝逻辑
} else if ("unionpay".equals(payType)) {
// 100行银联逻辑
}
// 每次新增都要在这里加else if
}
这种代码有三个致命问题:
- 每次新增支付方式都要修改核心业务类,违反开闭原则
- 所有支付逻辑耦合在一起,单个支付方式的单元测试无法隔离
- 合并请求时容易产生冲突,团队协作效率低下
1.1 策略模式的救赎
策略模式的本质是将算法家族封装成独立的类,使它们可以互相替换。就像游戏手柄的ABXY键——游戏机本身不需要知道当前按的是哪个键,只需要接收"按下"这个事件,具体执行什么操作由手柄按键映射决定。
在支付场景中,我们可以这样抽象:
- 策略接口:定义支付操作规范
java复制public interface PaymentStrategy {
PayResult pay(BigDecimal amount);
}
- 具体策略:实现各种支付方式
java复制public class WechatPayment implements PaymentStrategy {
@Override
public PayResult pay(BigDecimal amount) {
// 微信支付具体实现
}
}
- 上下文环境:维护策略引用
java复制public class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public PayResult executePay(BigDecimal amount) {
return strategy.pay(amount);
}
}
关键理解:策略模式不是简单的"把if-else变成类",而是建立了明确的抽象层,让高层模块不再依赖具体实现。这就像公司CEO不需要知道每个部门的工作细节,只需要通过部门经理这个抽象接口来协调工作。
2. 深度实战:电商促销系统改造
让我们通过一个真实的电商促销系统案例,看看如何用策略模式解决复杂业务规则。
2.1 原始代码分析
假设现有促销系统包含以下逻辑:
java复制public BigDecimal calculateDiscount(User user, Order order) {
if (user.isNewUser()) {
return order.getAmount().subtract(new BigDecimal(20));
} else if (user.getLevel() == UserLevel.VIP) {
return order.getAmount().multiply(new BigDecimal("0.9"));
} else if (order.getAmount().compareTo(new BigDecimal(100)) > 0) {
return order.getAmount().subtract(new BigDecimal(10));
}
// 更多条件分支...
}
随着业务发展,这段代码已经:
- 膨胀到500多行
- 包含各种特殊日期(双11、黑五)的特殊逻辑
- 难以添加新的促销类型
2.2 策略模式重构方案
2.2.1 定义策略体系
首先建立促销策略的抽象层次:
java复制public interface DiscountStrategy {
/**
* 计算折扣后价格
* @param order 订单信息
* @return 折扣信息(包含折扣金额、类型等)
*/
DiscountResult calculateDiscount(Order order);
}
2.2.2 实现具体策略
针对每种促销类型创建独立类:
java复制// 新用户首单立减
public class NewUserDiscount implements DiscountStrategy {
@Override
public DiscountResult calculateDiscount(Order order) {
if (order.getUser().isNewUser() && order.isFirstOrder()) {
return new DiscountResult(new BigDecimal(20), "NEW_USER");
}
return DiscountResult.NO_DISCOUNT;
}
}
// VIP折扣
public class VipDiscount implements DiscountStrategy {
@Override
public DiscountResult calculateDiscount(Order order) {
if (order.getUser().getLevel() == UserLevel.VIP) {
return new DiscountResult(
order.getAmount().multiply(new BigDecimal("0.1")),
"VIP_10%_OFF");
}
return DiscountResult.NO_DISCOUNT;
}
}
// 满减策略
public class OverAmountDiscount implements DiscountStrategy {
private final BigDecimal threshold;
private final BigDecimal discountAmount;
public OverAmountDiscount(BigDecimal threshold, BigDecimal amount) {
this.threshold = threshold;
this.discountAmount = amount;
}
@Override
public DiscountResult calculateDiscount(Order order) {
if (order.getAmount().compareTo(threshold) >= 0) {
return new DiscountResult(discountAmount, "OVER_AMOUNT");
}
return DiscountResult.NO_DISCOUNT;
}
}
2.2.3 策略组合与执行
引入策略组合模式处理多优惠叠加:
java复制public class CompositeDiscountStrategy implements DiscountStrategy {
private final List<DiscountStrategy> strategies;
public CompositeDiscountStrategy(List<DiscountStrategy> strategies) {
this.strategies = strategies;
}
@Override
public DiscountResult calculateDiscount(Order order) {
List<DiscountResult> results = new ArrayList<>();
for (DiscountStrategy strategy : strategies) {
DiscountResult result = strategy.calculateDiscount(order);
if (result.hasDiscount()) {
results.add(result);
}
}
return combineResults(results);
}
private DiscountResult combineResults(List<DiscountResult> results) {
// 实现优惠叠加逻辑(如折上折、互斥等)
}
}
2.3 策略工厂进阶实现
使用枚举+函数式接口创建智能工厂:
java复制public enum DiscountType {
NEW_USER(NewUserDiscount::new),
VIP(VipDiscount::new),
OVER_100_10(new OverAmountDiscount(new BigDecimal(100), new BigDecimal(10))),
SPECIAL_DAY(SpecialDayDiscount::new);
private final Supplier<DiscountStrategy> supplier;
DiscountType(Supplier<DiscountStrategy> supplier) {
this.supplier = supplier;
}
public DiscountStrategy getStrategy() {
return supplier.get();
}
}
public class DiscountStrategyFactory {
private static final Map<String, DiscountType> TYPE_MAPPING = Map.of(
"new_user", DiscountType.NEW_USER,
"vip", DiscountType.VIP,
"over100", DiscountType.OVER_100_10
);
public static DiscountStrategy getStrategy(String type) {
return Optional.ofNullable(TYPE_MAPPING.get(type))
.orElse(DiscountType.NEW_USER)
.getStrategy();
}
}
3. 生产环境中的最佳实践
3.1 性能优化方案
在策略模式的实际应用中,我们需要注意以下性能问题:
-
策略对象复用:
java复制// 无状态策略可使用单例 public enum SingletonStrategy implements DiscountStrategy { INSTANCE; @Override public DiscountResult calculateDiscount(Order order) { // 实现逻辑 } } -
避免频繁创建:
java复制// 使用对象池管理有状态策略 public class StrategyPool { private static final Map<Class<?>, ObjectPool<DiscountStrategy>> pools = new ConcurrentHashMap<>(); public static DiscountStrategy borrowStrategy(Class<? extends DiscountStrategy> clazz) { return pools.computeIfAbsent(clazz, k -> new GenericObjectPool<>(new BasePooledObjectFactory<>() { @Override public DiscountStrategy create() throws Exception { return clazz.newInstance(); } })) .borrowObject(); } }
3.2 与Spring框架的集成
在现代Java应用中,我们可以利用Spring的依赖注入特性来管理策略:
java复制// 策略接口
public interface ShippingStrategy {
ShippingResult calculate(Order order);
}
// 具体策略实现
@Component("standardShipping")
public class StandardShippingStrategy implements ShippingStrategy {
@Override
public ShippingResult calculate(Order order) {
// 标准物流计算
}
}
// 上下文服务
@Service
public class ShippingService {
private final Map<String, ShippingStrategy> strategyMap;
@Autowired
public ShippingService(Map<String, ShippingStrategy> strategyMap) {
this.strategyMap = strategyMap;
}
public ShippingResult calculateShipping(String strategyName, Order order) {
ShippingStrategy strategy = strategyMap.get(strategyName);
if (strategy == null) {
throw new IllegalArgumentException("Unknown strategy: " + strategyName);
}
return strategy.calculate(order);
}
}
3.3 动态策略切换实现
在某些场景下,我们需要支持运行时策略切换:
java复制public class DynamicStrategyContext {
private volatile DiscountStrategy currentStrategy;
public void updateStrategy(DiscountStrategy newStrategy) {
this.currentStrategy = newStrategy;
}
public DiscountResult applyDiscount(Order order) {
return currentStrategy.calculateDiscount(order);
}
}
// 配合配置中心实现热更新
@Configuration
public class StrategyConfiguration {
@Bean
@RefreshScope // 支持配置热更新
public DiscountStrategy discountStrategy(
@Value("${discount.strategy.type}") String type,
@Value("${discount.strategy.params}") String params) {
return StrategyFactory.createStrategy(type, params);
}
}
4. 复杂场景下的策略模式变体
4.1 带权重的策略路由
在处理多渠道分发时,我们可能需要根据权重选择策略:
java复制public class WeightedStrategyRouter {
private final List<StrategyWeight> strategies;
public WeightedStrategyRouter(List<StrategyWeight> strategies) {
this.strategies = strategies;
}
public DiscountStrategy selectStrategy() {
double totalWeight = strategies.stream()
.mapToDouble(StrategyWeight::getWeight)
.sum();
double random = Math.random() * totalWeight;
double accumulated = 0;
for (StrategyWeight entry : strategies) {
accumulated += entry.getWeight();
if (random <= accumulated) {
return entry.getStrategy();
}
}
return strategies.get(0).getStrategy();
}
@Data
@AllArgsConstructor
public static class StrategyWeight {
private DiscountStrategy strategy;
private double weight;
}
}
4.2 策略链模式
对于需要多步骤处理的场景,可以将策略组织成责任链:
java复制public class ChainOfStrategies implements DiscountStrategy {
private final List<DiscountStrategy> chain;
private final BiFunction<DiscountResult, DiscountResult, DiscountResult> combiner;
public ChainOfStrategies(List<DiscountStrategy> chain,
BiFunction<DiscountResult, DiscountResult, DiscountResult> combiner) {
this.chain = chain;
this.combiner = combiner;
}
@Override
public DiscountResult calculateDiscount(Order order) {
DiscountResult result = DiscountResult.NO_DISCOUNT;
for (DiscountStrategy strategy : chain) {
DiscountResult current = strategy.calculateDiscount(order);
result = combiner.apply(result, current);
}
return result;
}
}
4.3 策略模板方法
结合模板方法模式处理固定流程:
java复制public abstract class AbstractDiscountStrategy implements DiscountStrategy {
@Override
public final DiscountResult calculateDiscount(Order order) {
if (!isApplicable(order)) {
return DiscountResult.NO_DISCOUNT;
}
validate(order);
BigDecimal amount = calculateAmount(order);
return buildResult(order, amount);
}
protected abstract boolean isApplicable(Order order);
protected abstract void validate(Order order);
protected abstract BigDecimal calculateAmount(Order order);
protected DiscountResult buildResult(Order order, BigDecimal amount) {
return new DiscountResult(amount, getClass().getSimpleName());
}
}
5. 策略模式的边界与替代方案
5.1 何时不该使用策略模式
- 简单条件逻辑:当分支只有2-3个且不太可能扩展时
- 策略间高度耦合:如果策略需要频繁互相调用,可能状态模式更合适
- 性能敏感场景:策略对象创建开销可能成为瓶颈
5.2 替代方案对比
| 方案 | 适用场景 | 示例 |
|---|---|---|
| 策略模式 | 算法族需要自由切换 | 支付方式、促销策略 |
| 状态模式 | 对象行为随状态改变 | 订单状态流转 |
| 模板方法 | 固定流程可变步骤 | 数据导出流程 |
| 函数式接口 | 简单行为参数化 | 排序比较器 |
5.3 与规则引擎的配合
对于超复杂的业务规则,可以结合规则引擎:
java复制public class DroolsStrategy implements DiscountStrategy {
private final KieContainer kieContainer;
public DroolsStrategy(KieContainer kieContainer) {
this.kieContainer = kieContainer;
}
@Override
public DiscountResult calculateDiscount(Order order) {
KieSession session = kieContainer.newKieSession();
try {
session.insert(order);
List<DiscountResult> results = new ArrayList<>();
session.setGlobal("results", results);
session.fireAllRules();
return results.isEmpty() ?
DiscountResult.NO_DISCOUNT :
results.get(0);
} finally {
session.dispose();
}
}
}
在实际项目中,我经常看到开发者在初期过度设计,为简单的业务逻辑引入复杂的策略模式。我的经验法则是:当发现自己在复制粘贴if-else逻辑,或者经常修改同一段条件判断代码时,就是引入策略模式的最佳时机。