您问到了最关键的点子上,也是所有学习者从理论到实践最难跨越的一步。理论之所以感觉"一套套的",是因为它被系统地、干净地总结了出来。而现实是混乱、复杂且充满约束的。
下面,我抛弃纯理论,用一个 "实战推演" 的思路,带你看看设计模式是如何在实战中"自然生长"出来的。
核心理念:模式不是起点,而是重构的结果
不要带着"我要用模式"的心态去写代码,而要带着 "我要解决问题,让代码更干净" 的心态。模式是你到达终点后,别人给你起的名字。
实战推演:从一个简单需求开始
假设我们要开发一个电商系统的 OrderService,核心需求是创建订单。
第零步:最直接的实现
java
public class OrderService {
public Order createOrder(CreateOrderRequest request) {
// 1. 验证参数
if (request.getItems().isEmpty()) {
throw new ValidationException("订单项不能为空");
}
// 2. 计算价格
BigDecimal total = calculateTotal(request.getItems());
// 3. 扣减库存(调用库存服务)
inventoryClient.decreaseStock(request.getItems());
// 4. 保存订单到数据库
Order order = orderRepository.save(new Order(total, request.getItems()));
// 5. 发送订单创建成功事件(比如发邮件、短信)
eventBus.publish(new OrderCreatedEvent(order.getId()));
return order;
}
}
现状 :代码能工作,逻辑清晰。此时,任何设计模式都是多余的。
第一步:需求变化 -> 引入"策略模式"的种子
新需求:现在要支持不同类型的订单:普通订单、团购订单、秒杀订单。它们的价格计算规则完全不同。
糟糕的改法 :在 calculateTotal 方法里加 if-else。
java
// 坏味道:冗长的if-else
private BigDecimal calculateTotal(OrderType type, List<Item> items) {
if (type == OrderType.NORMAL) {
// ... 普通计算逻辑
} else if (type == OrderType.GROUP_BUY) {
// ... 团购计算逻辑
} else if (type == OrderType.FLASH_SALE) {
// ... 秒杀计算逻辑
}
// ... 未来每加一种类型,就要修改这里
}
问题:违反了"开闭原则"(对扩展开放,对修改关闭)。每次加新类型,都要修改这个核心类,风险高。
重构(模式自然浮现):
-
识别变化点:价格计算逻辑是变化的。
-
抽取接口 :定义一个
PricingStrategy接口。javapublic interface PricingStrategy { boolean supports(OrderType type); BigDecimal calculate(List<Item> items); } -
封装实现 :为每种订单类型创建一个策略类。
java@Component public class NormalPricingStrategy implements PricingStrategy { @Override public boolean supports(OrderType type) { return type == OrderType.NORMAL; } @Override public BigDecimal calculate(List<Item> items) { /* ... */ } } // ... 其他策略类 -
在Service中使用策略 :
java@Autowired private List<PricingStrategy> strategies; // Spring会自动注入所有实现 private BigDecimal calculateTotal(OrderType type, List<Item> items) { for (PricingStrategy strategy : strategies) { if (strategy.supports(type)) { return strategy.calculate(items); } } throw new UnsupportedOperationException("不支持的订单类型"); }
你看,我们并没有"使用策略模式",而是通过"消除重复的if-else"和"隔离变化",让策略模式的结构自然呈现了出来。 现在,增加新订单类型,我只需要新建一个 PricingStrategy 实现类,OrderService 完全不用动。
第二步:逻辑复杂 -> 引入"模板方法模式"的种子
新问题:创建订单的步骤越来越复杂,每种订单类型在核心步骤上一致(验证->算价->扣库存->保存->发事件),但某些步骤的实现细节不同(比如团购订单的验证逻辑更复杂)。
糟糕的改法 :在 createOrder 方法里写满针对类型的 if-else。代码会变成一团乱麻。
重构(模式自然浮现):
-
识别不变与变 :流程骨架(模板)是不变的 ,某些步骤的实现是可变的。
-
定义模板 :将固定流程提升到父类。
javapublic abstract class AbstractOrderCreator { // 这就是"模板方法" public final Order createOrder(CreateOrderRequest request) { // 固定流程 validateSpecific(request); BigDecimal total = calculateTotal(request); decreaseStock(request); Order order = saveOrder(request, total); publishEvent(order); return order; } // 将变化的步骤声明为抽象方法,强迫子类实现 protected abstract void validateSpecific(CreateOrderRequest request); protected abstract BigDecimal calculateTotal(CreateOrderRequest request); // ... 其他抽象或可重写的方法 } -
创建子类 :
java@Service public class GroupBuyOrderCreator extends AbstractOrderCreator { @Override protected void validateSpecific(CreateOrderRequest request) { // 团购订单特有的复杂验证逻辑 if (!groupBuyService.isValid(request.getGroupId())) { throw new ValidationException("团购活动已失效"); } // ... 更多验证 } // ... 实现其他抽象方法 }
你看,我们为了解决"流程相同,部分步骤不同"的问题,自然地使用了模板方法模式。 现在,OrderService 的职责可以简化为路由,根据类型找到对应的 AbstractOrderCreator 来执行。
第三步:依赖混乱 -> 引入"依赖注入"与"门面模式"
新问题 :OrderService 现在依赖了太多东西:各种 Creator、PricingStrategy、仓库、客户端等。它变成了一个"上帝类",难以测试和维护。
重构(模式自然浮现):
-
应用依赖注入 :这不是一个具体的GoF模式,而是一个更基础的架构原则。我们通过构造函数清晰地声明依赖,而不是用
@Autowired到处乱注。java@Service public class OrderService { private final Map<OrderType, AbstractOrderCreator> creatorMap; // 通过构造器注入,依赖关系一目了然 public OrderService(List<AbstractOrderCreator> creators) { this.creatorMap = creators.stream() .collect(Collectors.toMap(creator -> creator.supportsOrderType(), Function.identity())); } public Order createOrder(CreateOrderRequest request) { AbstractOrderCreator creator = creatorMap.get(request.getType()); return creator.createOrder(request); } } -
考虑门面模式 :如果外部客户端(如Controller)需要调用多个Service才能完成一个操作,我们可以引入一个
OrderFacade,为外部提供一个统一的、简洁的接口,隐藏内部OrderService、PaymentService、ShippingService的复杂交互。
给你的实践路线图
- 忘记模式,记住原则 :首先牢记 SOLID 原则(尤其是单一职责SRP和开闭原则OCP)和 DRY(不要重复自己)。这些是产生模式的土壤。
- 从小处着手:不要试图设计一个完美的系统。先让代码跑起来。
- 培养嗅觉 :学会识别"代码坏味道"。
- 重复代码 -> 提取方法/类。
- 过长的函数/类 -> 分解。
- 庞大的条件判断 -> 考虑用多态(策略、状态模式)替代。
- 类知道得太多 -> 考虑依赖注入、中介者模式。
- 痛苦驱动重构:当添加新功能或修改代码变得非常困难和痛苦时,就是重构的最佳时机。问问自己:"这里是什么变化让我如此痛苦?我能否将这部分抽离出来?"
- 重构,而非重写:通过一系列小步、安全的重构(有测试保障最好),将"坏代码"逐步改善为"好代码"。在这个过程中,你会发现设计模式的结构自然而然地出现了。
最终,你的目标不是"在代码中使用模式",而是"写出干净的代码"。而干净的代码,往往会自然而然地体现出经典设计模式的结构。 这时,模式对你来说就不再是"一套套的理论",而是你工具箱里顺手拈来的、有名字的工具了。