1. 设计模式组合拳:策略+模版方法+工厂模式实战解析
在软件工程领域,设计模式就像武术中的组合拳法,单独使用每个招式固然有效,但真正的威力往往来自于模式的有机组合。最近我在重构一个电商促销系统时,就遇到了需要同时应用策略模式、模版方法模式和工厂模式的场景。当这三种模式协同工作时,代码的扩展性和可维护性得到了质的提升。
这个系统需要处理各种促销活动(满减、折扣、赠品等),每种活动都有不同的计算规则,但又有共性的流程(验证资格、计算优惠、记录日志等)。通过策略模式封装算法变化点,模版方法固定流程骨架,工厂模式统一创建入口,最终实现了新增促销类型只需添加新类而不修改现有代码的优雅架构。下面我就详细拆解这个组合模式的实际应用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 策略模式:封装算法家族
2.1 策略模式的核心思想
策略模式(Strategy Pattern)定义了算法家族,分别封装起来,让它们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户端。在我的电商案例中,各种促销优惠计算就是典型的算法变化点。
java复制// 策略接口
public interface PromotionStrategy {
BigDecimal calculateDiscount(Order order);
}
// 具体策略实现
public class FullReductionStrategy implements PromotionStrategy {
@Override
public BigDecimal calculateDiscount(Order order) {
// 满100减20的具体实现
}
}
public class PercentageStrategy implements PromotionStrategy {
@Override
public BigDecimal calculateDiscount(Order order) {
// 打8折的具体实现
}
}
2.2 策略选择的动态性
策略模式的精髓在于运行时动态切换算法。我们通常会维护一个策略映射表,根据业务场景选择对应策略:
java复制public class PromotionContext {
private static final Map<String, PromotionStrategy> strategies = Map.of(
"FULL_REDUCTION", new FullReductionStrategy(),
"PERCENTAGE", new PercentageStrategy()
);
public BigDecimal applyPromotion(String type, Order order) {
return strategies.get(type).calculateDiscount(order);
}
}
实际项目中建议使用Spring的依赖注入来管理策略对象,避免手动维护映射关系
3. 模版方法模式:定义算法骨架
3.1 固定流程中的变化点
模版方法模式(Template Method Pattern)在抽象类中定义算法的骨架,将一些步骤延迟到子类实现。这使得子类可以不改变算法结构的情况下重新定义某些步骤。
在我们的促销系统中,虽然各种促销的计算逻辑不同,但处理流程是固定的:
- 验证用户资格
- 计算优惠金额
- 记录促销日志
- 返回计算结果
java复制public abstract class AbstractPromotionTemplate {
// 模版方法设为final防止子类覆盖
public final BigDecimal process(Order order) {
validate(order);
BigDecimal discount = calculate(order);
logResult(order, discount);
return discount;
}
protected abstract void validate(Order order);
protected abstract BigDecimal calculate(Order order);
private void logResult(Order order, BigDecimal discount) {
// 公共日志记录逻辑
}
}
3.2 钩子方法的灵活运用
模版方法模式还可以通过钩子方法(Hook Method)提供额外扩展点:
java复制public abstract class AbstractPromotionTemplate {
// ...其他代码
protected boolean needExtraCheck() {
return false; // 默认不进行额外检查
}
protected void extraCheck(Order order) {
// 空实现
}
}
子类可以选择性覆盖这些钩子方法来增加特定行为,而不影响主流程。
4. 工厂模式:统一对象创建
4.1 简单工厂的实用变体
工厂模式(Factory Pattern)负责创建对象,而不需要暴露实例化逻辑。在我们的场景中,结合策略模式和模版方法,可以使用改进的简单工厂:
java复制public class PromotionFactory {
public static PromotionStrategy createStrategy(String type) {
switch (type) {
case "FULL_REDUCTION":
return new FullReductionStrategy();
case "PERCENTAGE":
return new PercentageStrategy();
default:
throw new IllegalArgumentException("未知促销类型");
}
}
public static AbstractPromotionTemplate createTemplate(String type) {
switch (type) {
case "FULL_REDUCTION":
return new FullReductionTemplate();
case "PERCENTAGE":
return new PercentageTemplate();
default:
throw new IllegalArgumentException("未知促销类型");
}
}
}
4.2 工厂方法的高级应用
对于更复杂的场景,可以使用标准的工厂方法模式,让子类决定实例化哪个类:
java复制public interface PromotionFactory {
PromotionStrategy createStrategy();
AbstractPromotionTemplate createTemplate();
}
public class FullReductionFactory implements PromotionFactory {
@Override
public PromotionStrategy createStrategy() {
return new FullReductionStrategy();
}
@Override
public AbstractPromotionTemplate createTemplate() {
return new FullReductionTemplate();
}
}
5. 模式组合的实战架构
5.1 三层架构设计
将三种模式有机结合,可以构建出清晰的三层架构:
- 工厂层:负责创建具体的策略和模版对象
- 模版层:定义处理流程骨架
- 策略层:实现具体的算法逻辑
mermaid复制classDiagram
class PromotionFactory {
+createStrategy(String type) PromotionStrategy
+createTemplate(String type) AbstractPromotionTemplate
}
class AbstractPromotionTemplate {
<<abstract>>
+process(Order order) BigDecimal
#validate(Order order)
#calculate(Order order)
}
class PromotionStrategy {
<<interface>>
+calculateDiscount(Order order) BigDecimal
}
PromotionFactory --> AbstractPromotionTemplate
PromotionFactory --> PromotionStrategy
AbstractPromotionTemplate --> PromotionStrategy
5.2 实际应用示例
在Spring Boot项目中的典型应用:
java复制@Service
public class PromotionService {
@Autowired
private PromotionFactory factory;
public BigDecimal applyPromotion(String type, Order order) {
AbstractPromotionTemplate template = factory.createTemplate(type);
return template.process(order);
}
}
// 配置类注册所有策略和模版
@Configuration
public class PromotionConfig {
@Bean
public FullReductionStrategy fullReductionStrategy() {
return new FullReductionStrategy();
}
@Bean
public FullReductionTemplate fullReductionTemplate() {
return new FullReductionTemplate();
}
// 其他策略和模版的bean定义...
}
6. 性能优化与注意事项
6.1 对象复用策略
频繁创建策略对象可能影响性能,可以考虑以下优化:
- 无状态策略对象复用:如果策略对象是无状态的,可以设计为单例
- 对象池技术:对于有状态但创建成本高的策略对象
- 原型模式结合:通过克隆避免重复初始化
java复制public class PromotionStrategyPool {
private static final Map<String, PromotionStrategy> pool = new ConcurrentHashMap<>();
public static PromotionStrategy getStrategy(String type) {
return pool.computeIfAbsent(type, PromotionFactory::createStrategy);
}
}
6.2 模式选择的考量
不是所有场景都适合这种组合,需要考虑:
- 算法复杂度:简单算法直接if-else可能更合适
- 变化频率:不常变化的逻辑不需要过度设计
- 团队熟悉度:复杂模式需要团队有共识
在最近的一个支付网关项目中,我们评估后放弃了这种组合模式,因为支付方式虽然多样但计算逻辑都非常简单,使用策略模式反而增加了不必要的复杂度。
7. 测试策略与调试技巧
7.1 单元测试要点
测试这种组合模式时需要注意:
- 策略测试:单独测试每个具体策略的实现
- 模版测试:验证流程是否正确执行所有步骤
- 集成测试:检查工厂创建的对象是否符合预期
java复制class FullReductionStrategyTest {
@Test
void shouldCalculateCorrectDiscount() {
PromotionStrategy strategy = new FullReductionStrategy();
Order order = new Order(new BigDecimal("120"));
BigDecimal discount = strategy.calculateDiscount(order);
assertEquals(new BigDecimal("20"), discount);
}
}
class AbstractPromotionTemplateTest {
@Test
void shouldExecuteAllSteps() {
AbstractPromotionTemplate template = new AbstractPromotionTemplate() {
@Override protected void validate(Order order) {}
@Override protected BigDecimal calculate(Order order) {
return BigDecimal.TEN;
}
};
BigDecimal result = template.process(new Order());
assertEquals(BigDecimal.TEN, result);
// 验证日志等副作用
}
}
7.2 调试复杂组合
当多种模式组合时,调试可能会变得复杂。我的经验是:
- 给每个策略和模版添加toString():方便日志输出时识别
- 使用责任链模式包装:可以在每个步骤前后插入日志
- 可视化流程工具:如Arthas跟踪方法调用链
java复制public abstract class AbstractPromotionTemplate {
// ...
@Override
public String toString() {
return getClass().getSimpleName();
}
}
8. 扩展与变体
8.1 结合其他模式
在实际项目中,这种组合还可以进一步扩展:
- 装饰器模式:为策略添加额外功能(如缓存、重试)
- 责任链模式:组合多个策略形成处理链
- 观察者模式:在模版方法的关键节点触发事件
java复制// 装饰器示例:带缓存的策略
public class CachedPromotionStrategy implements PromotionStrategy {
private final PromotionStrategy wrapped;
private final Cache cache;
public CachedPromotionStrategy(PromotionStrategy wrapped) {
this.wrapped = wrapped;
this.cache = CacheBuilder.newBuilder().build();
}
@Override
public BigDecimal calculateDiscount(Order order) {
String key = order.getId() + "-" + wrapped.getClass().getSimpleName();
return cache.get(key, () -> wrapped.calculateDiscount(order));
}
}
8.2 领域特定语言(DSL)
对于配置复杂的策略,可以考虑实现DSL:
java复制PromotionStrategy strategy = PromotionDSL.create()
.when(order -> order.getAmount().compareTo(BigDecimal.valueOf(100)) > 0)
.thenApply(new FullReductionStrategy())
.otherwise(new PercentageStrategy());
这种组合模式的应用远不止于电商促销系统。在我参与过的多个项目中,包括金融风控系统(不同风险等级的评估策略)、游戏引擎(不同AI行为模式)、报表生成系统(不同格式的输出策略)等都成功应用了这种模式组合。关键在于识别出哪些是变化的算法(策略模式)、哪些是固定的流程(模版方法)、以及如何统一管理这些对象的创建(工厂模式)。
