1. 从冗余代码到设计模式:一个实战案例的启示
上周review同事的订单系统代码时,我遇到了一个典型的"模式滥用"案例:同一个订单创建逻辑里,既有简单工厂创建基础订单,又有抽象工厂生成国际订单,最后还混着建造者模式添加优惠信息。三种创建型模式堆砌在一起,不仅没带来灵活性,反而让代码变成了一团乱麻。这促使我思考:如何正确运用创建型模式实现真正的优化?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 创建型模式家族图谱解析
2.1 五大金刚的职责边界
- 简单工厂:适用于对象创建逻辑简单且变化少的场景(如根据类型字段创建不同图表对象)
- 工厂方法:将具体产品创建延迟到子类(如跨平台UI组件创建)
- 抽象工厂:生产相关产品族(如不同风格的整套GUI控件)
- 建造者:分步构建复杂对象(如包含校验规则的订单对象)
- 原型:通过克隆避免重复初始化开销(如游戏场景中大量相似NPC)
2.2 模式选择的决策树
mermaid复制graph TD
A[需要创建对象?] -->|是| B{创建逻辑复杂?}
B -->|否| C[简单工厂]
B -->|是| D{需要配置构建步骤?}
D -->|是| E[建造者]
D -->|否| F{产品族关联?}
F -->|是| G[抽象工厂]
F -->|否| H[工厂方法]
关键原则:能用简单工厂解决的不用工厂方法,需要产品族协作时才上抽象工厂
3. 电商订单系统的模式优化实战
3.1 原始代码的问题诊断
java复制// 反例:三种模式混用的订单服务
public Order createOrder(OrderDTO dto) {
// 简单工厂创建基础订单
Order order = OrderFactory.create(dto.getType());
if(order.isInternational()) {
// 抽象工厂创建跨境物流
LogisticsFactory.getInternationalFactory()
.createShipping(order);
}
// 建造者添加优惠信息
return new OrderBuilder(order)
.addDiscount(dto.getCoupon())
.build();
}
主要问题:
- 简单工厂与建造者重复初始化订单对象
- 跨境物流创建与订单强耦合
- 优惠逻辑本应属于业务规则而非创建过程
3.2 重构后的模式组合方案
java复制// 正例:单一职责的模式组合
public Order createOrder(OrderDTO dto) {
// 工厂方法处理多类型订单创建
Order order = createConcreteOrder(dto);
// 策略模式处理优惠(非创建逻辑)
applyDiscountStrategy(order, dto.getCoupon());
return order;
}
// 子类可扩展的工厂方法
protected abstract Order createConcreteOrder(OrderDTO dto);
// 单独物流服务
public Shipping createShipping(Order order) {
return LogisticsFactory
.getFactory(order.getRegion())
.createShipping();
}
优化点:
- 用工厂方法替代简单工厂+建造者
- 将物流创建移出订单服务
- 优惠逻辑改用策略模式实现
4. 模式冗余的六种典型症状
4.1 创建逻辑的"俄罗斯套娃"
java复制// 建造者内部又套工厂方法
new PizzaBuilder()
.withBase(PizzaFactory.createBase(type))
.addTopping(ToppingFactory.create(name))
.build();
修复方案:建造者内部直接new对象,保持创建层次扁平化
4.2 过度设计的抽象工厂
当产品只有1-2个且无扩展需求时,抽象工厂反而增加复杂度:
java复制// 不必要的抽象工厂
interface GUIFactory {
Button createButton();
// 实际只用到了Button...
}
4.3 原型模式的误用场景
在对象初始化成本不高时使用原型模式:
java复制// 反例:轻量级配置对象也用克隆
Config config = prototypeConfig.clone();
// 正解:直接new更清晰
Config config = new Config(defaults);
5. 模式组合的最佳实践
5.1 建造者+工厂方法的高效配合
适用于需要分步构建的复杂对象族:
java复制// 报表生成器示例
ReportBuilder builder = ReportFactory
.getBuilder(reportType); // 工厂方法选择建造者
builder.setDataSource(db)
.applyTheme(theme)
.build();
5.2 原型注册表+抽象工厂
实现可配置的产品族创建:
java复制// 游戏道具生成系统
public class ItemFactory {
private Map<String, ItemPrototype> prototypes;
public Item createItem(String type) {
return prototypes.get(type).clone();
}
}
6. 性能优化中的模式取舍
6.1 对象池vs原型模式
| 维度 | 对象池 | 原型模式 |
|---|---|---|
| 适用场景 | 初始化成本极高的对象 | 中等初始化成本对象 |
| 内存占用 | 固定预分配 | 按需克隆 |
| 线程安全 | 需要同步控制 | 每次克隆新实例 |
6.2 缓存工厂的实现技巧
java复制public class CachedFactory {
private static final Map<String, Product> cache =
new ConcurrentHashMap<>();
public Product getProduct(String key) {
return cache.computeIfAbsent(key, k -> {
Product p = createProduct(k);
// 初始化后处理...
return p;
});
}
}
注意事项:
- 缓存的对象需要是immutable的
- 考虑引入软引用防止内存泄漏
- 需要提供缓存清除机制
7. 从模式到原则的升华
7.1 识别真正的变化点
在电商促销系统重构中,我们发现:
- 促销规则(满减/折扣/赠品)是高频变化点 → 策略模式
- 促销资格校验相对稳定 → 模板方法模式
- 促销创建本身变化不大 → 简单工厂足矣
7.2 模式最小化实践
我总结的"三步精简法":
- 先用最简单的new创建对象
- 当出现重复创建逻辑时提取简单工厂
- 只有明确需要扩展点时才引入工厂方法
经验法则:在系统演进过程中逐步引入模式,而不是一开始就过度设计
8. 工具链的辅助优化
8.1 ArchUnit检测模式滥用
java复制// 检测建造者模式误用
ArchRule rule = noClasses()
.that().haveNameMatching(".*Builder")
.should().dependOnClassesThat()
.haveNameMatching(".*Factory");
rule.check(importedClasses);
8.2 JProfiler分析模式开销
通过CPU热点分析发现:
- 抽象工厂的层级调用在简单场景产生5%额外开销
- 原型模式在复杂对象场景节省40%创建时间
9. 复杂系统中的模式治理
在微服务架构下,我们建立了这样的规范:
- 基础服务层:允许使用简单工厂/建造者
- 业务中台层:推荐工厂方法+策略模式
- 网关层:强制使用抽象工厂保证扩展性
配合代码扫描插件,当检测到Controller层出现new关键字时自动提示:"是否考虑工厂方法?"
10. 我的踩坑日记
最近在IoT设备管理系统中的教训:
- 为20种设备类型实现了抽象工厂,后来发现80%设备创建逻辑相同
- 改用工厂方法+默认实现后,代码量减少65%
- 意外的收获:设备联动配置变得简单,因为不再需要处理多层级工厂
关键收获:模式不是银弹,持续重构比完美设计更重要
