☰
策略模式与工厂模式在电商促销系统的实战应用
2026/10/14 14:40:03 网站建设 项目流程

1. 策略模式与工厂模式的黄金组合

在面向对象编程中,设计模式就像老厨师手中的秘制配方,能让代码烹饪出更美味的架构。最近我在重构一个电商促销系统时,发现策略模式+工厂模式的组合拳特别适合处理复杂的业务规则变化。这种搭配既能保持代码的扩展性,又能简化客户端调用,就像给代码装上了自动挡变速箱。

想象一下这样的场景:你的系统需要支持满减、折扣、赠品等多种促销策略,而且未来还会不断增加新玩法。如果直接用if-else堆砌,代码很快就会变成难以维护的意大利面条。而策略模式将每种算法封装成独立类,工厂模式则负责隐藏对象创建细节,二者结合产生的化学反应会让系统具备惊人的弹性。

2. 模式原理深度解析

2.1 策略模式的精髓

策略模式的核心在于定义算法族,将每个算法封装起来,使它们可以互相替换。这种模式让算法的变化独立于使用算法的客户端,就像游戏手柄上的ABXY键,每个按键对应不同技能,但操作方式完全一致。

在Java中实现策略模式通常包含三个角色:

  • 策略接口(Strategy):定义所有支持的算法接口
  • 具体策略(ConcreteStrategy):实现策略接口的具体算法类
  • 上下文(Context):持有一个策略引用,提供接口给客户端调用
// 策略接口 public interface PromotionStrategy { BigDecimal apply(BigDecimal price); } // 具体策略实现 public class DiscountStrategy implements PromotionStrategy { private final BigDecimal rate; public DiscountStrategy(BigDecimal rate) { this.rate = rate; } @Override public BigDecimal apply(BigDecimal price) { return price.multiply(rate); } }

2.2 工厂模式的妙用

简单工厂模式虽然不在GoF 23种设计模式之列,但在实际开发中非常实用。它就像个智能快递柜,客户端只需要告诉工厂"我要取促销策略A",工厂就会返回对应的策略实例,完全不用关心这个策略是怎么创建的。

工厂模式主要解决以下问题:

  • 对象创建逻辑集中管理
  • 客户端与具体实现解耦
  • 便于统一修改创建逻辑
public class StrategyFactory { private static final Map<String, PromotionStrategy> STRATEGIES = new HashMap<>(); static { STRATEGIES.put("DISCOUNT", new DiscountStrategy(new BigDecimal("0.8"))); STRATEGIES.put("FULL_REDUCTION", new FullReductionStrategy(100, 20)); } public static PromotionStrategy getStrategy(String type) { return STRATEGIES.get(type); } }

3. 实战:电商促销系统改造

3.1 原始代码的问题诊断

在改造前的促销系统中,订单服务直接包含了所有促销逻辑:

public class OrderService { public BigDecimal calculatePrice(String promoType, BigDecimal price) { if ("DISCOUNT".equals(promoType)) { return price.multiply(new BigDecimal("0.8")); } else if ("FULL_REDUCTION".equals(promoType)) { if (price.compareTo(new BigDecimal(100)) >= 0) { return price.subtract(new BigDecimal(20)); } } // 更多if-else... return price; } }

这段代码存在明显问题:

  1. 违反开闭原则:新增策略需要修改现有代码
  2. 方法职责过重:包含所有促销逻辑
  3. 难以单元测试:各种条件分支交织在一起

3.2 重构后的优雅实现

首先定义策略接口和具体实现:

public interface PromotionStrategy { String getType(); BigDecimal apply(BigDecimal price); } public class DiscountStrategy implements PromotionStrategy { private final BigDecimal rate; public DiscountStrategy(BigDecimal rate) { this.rate = rate; } @Override public String getType() { return "DISCOUNT"; } @Override public BigDecimal apply(BigDecimal price) { return price.multiply(rate); } }

然后实现策略工厂,这里使用枚举+Map优化:

public class StrategyFactory { private static final Map<String, PromotionStrategy> STRATEGY_MAP = new HashMap<>(); static { registerStrategy(new DiscountStrategy(new BigDecimal("0.8"))); registerStrategy(new FullReductionStrategy(100, 20)); } private static void registerStrategy(PromotionStrategy strategy) { STRATEGY_MAP.put(strategy.getType(), strategy); } public static PromotionStrategy getStrategy(String type) { PromotionStrategy strategy = STRATEGY_MAP.get(type); if (strategy == null) { throw new IllegalArgumentException("Unknown strategy type: " + type); } return strategy; } }

最终订单服务变得极其简洁:

public class OrderService { public BigDecimal calculatePrice(String promoType, BigDecimal price) { PromotionStrategy strategy = StrategyFactory.getStrategy(promoType); return strategy.apply(price); } }

4. 进阶优化技巧

4.1 使用Spring优化工厂实现

在Spring项目中,我们可以利用依赖注入和Bean特性实现更优雅的工厂:

@Component public class StrategyFactory { private final Map<String, PromotionStrategy> strategyMap; @Autowired public StrategyFactory(List<PromotionStrategy> strategies) { strategyMap = strategies.stream() .collect(Collectors.toMap( PromotionStrategy::getType, Function.identity() )); } public PromotionStrategy getStrategy(String type) { return Optional.ofNullable(strategyMap.get(type)) .orElseThrow(() -> new IllegalArgumentException("Unknown strategy type: " + type)); } }

4.2 策略模式的线程安全

策略对象通常是无状态的,可以安全地在多线程环境下共享。但如果策略需要维护状态,则需要考虑线程安全问题:

public class CounterStrategy implements PromotionStrategy { private final AtomicInteger counter = new AtomicInteger(0); @Override public BigDecimal apply(BigDecimal price) { counter.incrementAndGet(); return price.subtract(BigDecimal.valueOf(counter.get())); } }

4.3 组合策略的实现

有时需要组合多个策略,可以通过装饰器模式实现:

public class CompositeStrategy implements PromotionStrategy { private final List<PromotionStrategy> strategies; public CompositeStrategy(PromotionStrategy... strategies) { this.strategies = Arrays.asList(strategies); } @Override public BigDecimal apply(BigDecimal price) { BigDecimal result = price; for (PromotionStrategy strategy : strategies) { result = strategy.apply(result); } return result; } }

5. 避坑指南与最佳实践

5.1 常见问题排查

  1. 策略未生效:

    • 检查工厂注册的策略类型是否与传入类型完全匹配(注意大小写)
    • 确保没有遗漏策略类的@Component注解(Spring环境)
    • 在工厂初始化后打印所有注册的策略类型进行验证
  2. NPE异常:

    • 工厂方法应对未知策略类型返回默认策略或抛出明确异常
    • 使用Optional包装返回值可以更安全
  3. 性能问题:

    • 避免在策略实现中创建大量临时对象
    • 考虑使用策略对象池(对象创建成本高时)

5.2 设计注意事项

  1. 策略粒度控制:

    • 策略划分过粗会导致单个策略过于复杂
    • 策略划分过细会增加系统复杂度
    • 建议按业务维度划分,保持单一职责
  2. 工厂管理范围:

    • 不要将所有策略都放在一个工厂中
    • 可以按业务域划分多个工厂类
    • 或者使用分层工厂结构
  3. 与其它模式配合:

    • 结合模板方法模式处理策略中的公共逻辑
    • 使用装饰器模式实现策略组合
    • 通过责任链模式实现策略的链式调用

5.3 测试建议

  1. 单元测试策略:

    • 为每个策略实现编写独立的测试用例
    • 测试边界条件和异常情况
  2. 工厂测试要点:

    • 验证工厂返回的策略实例类型正确
    • 测试传入无效类型时的异常处理
  3. 集成测试场景:

    • 模拟真实调用链验证策略效果
    • 测试策略组合使用的正确性
@Test void testDiscountStrategy() { PromotionStrategy strategy = new DiscountStrategy(new BigDecimal("0.8")); assertEquals(new BigDecimal("80.00"), strategy.apply(new BigDecimal("100.00"))); } @Test void testStrategyFactory() { PromotionStrategy strategy = StrategyFactory.getStrategy("DISCOUNT"); assertTrue(strategy instanceof DiscountStrategy); assertThrows(IllegalArgumentException.class, () -> { StrategyFactory.getStrategy("UNKNOWN"); }); }

6. 模式应用场景扩展

6.1 支付网关集成

支付渠道(支付宝、微信、银联)的选择是策略模式的经典应用场景:

public interface PaymentStrategy { PaymentResult pay(PaymentRequest request); } @Service public class AlipayStrategy implements PaymentStrategy { @Override public PaymentResult pay(PaymentRequest request) { // 调用支付宝SDK } } @Service public class WechatPayStrategy implements PaymentStrategy { @Override public PaymentResult pay(PaymentRequest request) { // 调用微信支付API } }

6.2 报表生成引擎

不同格式的报表生成(PDF、Excel、HTML)也适合使用策略模式:

public interface ReportGenerator { byte[] generate(ReportData data); } @Component public class PdfReportGenerator implements ReportGenerator { @Override public byte[] generate(ReportData data) { // 使用iText等库生成PDF } }

6.3 运费计算系统

电商中的运费计算规则经常变化,策略模式可以轻松应对:

public interface ShippingCalculator { BigDecimal calculate(Order order); } @Component public class FreeShippingCalculator implements ShippingCalculator { @Override public BigDecimal calculate(Order order) { return order.getTotal().compareTo(BigDecimal.valueOf(199)) >= 0 ? BigDecimal.ZERO : BigDecimal.TEN; } }

在实际项目中,策略+工厂的组合就像瑞士军刀,特别适合处理以下场景:

  • 业务规则需要频繁变更或扩展
  • 系统中存在多重条件分支
  • 需要动态选择算法实现
  • 希望隔离算法实现和使用代码

这种模式组合的威力在于,当新增一种策略时,你只需要新增一个策略类并在工厂中注册,完全不用修改现有代码。就像乐高积木一样,系统的扩展变得轻松而优雅。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询