Skip to content
设计模式与结构抽象 · 第 2 篇 / 共 3 篇
领域编程与框架
专题架构与设计专题
当前序列设计模式与结构抽象
阅读位置第 2 篇 / 共 3 篇当前专题第 1 个序列 / 共 3 个序列

工厂模式怎么选:简单工厂、工厂方法、抽象工厂的边界

很多项目刚开始写的时候,对象创建都很直接:

java
PayService payService = new WechatPayService();

这样写并没有错,但当创建逻辑越来越复杂、实现类越来越多时,业务代码就会开始被 new 污染。

这时候,工厂模式才真正有价值。

先说结论

如果只记一个实用判断:

  • 简单工厂:实现类不多,想集中创建逻辑
  • 工厂方法:不同产品的创建过程差异变大
  • 抽象工厂:一组相关产品需要一起创建

不要为了“用模式而用模式”,关键还是看对象创建有没有成为变化点。

一、为什么需要工厂模式

如果对象创建逻辑分散在业务代码里,后面通常会遇到这些问题:

  • 业务类知道太多实现细节
  • 实现类一变,调用方到处要改
  • 创建过程带配置、校验、初始化时,代码会越来越乱

工厂模式的本质,是把“创建对象”这件事从业务流程里拆出去。

二、简单工厂:先把创建逻辑集中起来

简单工厂最直观的做法,就是由一个工厂类统一负责创建对象。

java
public interface Sender {
    void send();
}

public class MailSender implements Sender {
    @Override
    public void send() {
        System.out.println("mail");
    }
}

public class SmsSender implements Sender {
    @Override
    public void send() {
        System.out.println("sms");
    }
}

public class SenderFactory {
    public Sender create(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        }
        if ("sms".equals(type)) {
            return new SmsSender();
        }
        throw new IllegalArgumentException("unsupported type: " + type);
    }
}

简单工厂适合什么时候用

  • 产品类型有限
  • 创建逻辑比较简单
  • 想先把散落的 new 收口

它的边界在哪里

最大问题是:

  • 新增产品时,工厂类本身要改

也就是说,它违背了“对扩展开放、对修改关闭”的方向。

另外,很多旧笔记里会用字符串来区分类型,这种写法能跑,但不够稳,至少应该:

  • 用枚举代替裸字符串
  • 非法类型时主动抛异常

三、工厂方法:让每种产品有自己的工厂

当不同产品的创建流程已经明显不一样时,继续把所有逻辑塞进一个工厂类里,就会越来越臃肿。

这时可以考虑工厂方法。

java
public interface SenderFactory {
    Sender create();
}

public class MailSenderFactory implements SenderFactory {
    @Override
    public Sender create() {
        return new MailSender();
    }
}

public class SmsSenderFactory implements SenderFactory {
    @Override
    public Sender create() {
        return new SmsSender();
    }
}

它解决了什么问题

  • 新增产品时,通常只要新增实现类和对应工厂
  • 单个工厂职责更清晰

它的代价是什么

  • 类会明显变多
  • 如果产品规模不大,可能显得有点重

所以工厂方法不是“永远比简单工厂高级”,而是更适合变化已经变复杂的时候。

四、抽象工厂:一次创建一整套相关产品

如果你的系统不是创建一个对象,而是创建“同一风格的一整套对象”,抽象工厂才有意义。

例如:

  • 不同数据库驱动下的一组连接对象
  • 不同 UI 风格下的一组组件
  • 不同支付渠道下的一组适配对象

抽象工厂更强调的是:

  • 一组相关对象要成套出现

这已经不是“创建单个产品”的问题,而是“产品族”的问题。

五、工程里怎么选更稳

可以用一个很实用的判断顺序:

1. 先问对象创建是否已经成了痛点

如果创建逻辑还很简单,直接 new 并不一定有问题。

2. 如果只是想把创建逻辑集中起来

先用简单工厂就够了。

3. 如果不同产品的创建流程差异越来越大

考虑工厂方法。

4. 如果你要管理的是一组有关联的产品

再考虑抽象工厂。

六、Java 和 Spring 里哪些地方能看到工厂思想

工厂模式并不只存在于设计模式教材里。

在 Java 和 Spring 里,工厂思想非常常见:

  • BeanFactory
  • FactoryBean
  • 各种 Builder / Client 的创建器

很多框架并不是完整照搬某一种模式,而是把工厂思想揉进了自己的设计里。

七、几个容易踩的坑

1. 用字符串区分所有产品

这种写法很常见,但扩展性和可维护性都一般。

2. 工厂类里又开始写业务逻辑

工厂应该只关心创建,不该承担过多业务判断。

3. 只有两个实现类也上复杂抽象

抽象不是越早越好,而是变化真的出现时再做更值。

一句话总结

工厂模式的重点,不是背“有三种工厂”,而是判断对象创建是不是已经成了变化点。

变化不大时,简单工厂足够;变化越来越复杂时,再考虑工厂方法和抽象工厂。

延伸阅读相关文章优先当前专题,再补跨专题关联。
同一序列 · 顺着当前主线继续读单例模式怎么写更稳适合把设计模式、对象创建和结构抽象能力放在一组里系统理解。架构与设计专题 · 设计模式与结构抽象同一序列 · 回看前文会更完整设计模式总览适合把设计模式、对象创建和结构抽象能力放在一组里系统理解。架构与设计专题 · 设计模式与结构抽象同专题其他序列 · 共享标签:选型对比令牌桶、漏桶、滑动窗口限流怎么选适合把幂等、限流、分布式 ID 和一致性模型放在同一条架构主线上看。架构与设计专题 · 分布式系统设计基线同专题其他序列 · 共享标签:选型对比Saga、TCC、Outbox 怎么选适合把 DDD、Saga、TCC 和 BFF、网关边界放在一起看。架构与设计专题 · 分布式系统取舍与边界跨专题关联 · 同场景:方案选型半同步复制和异步复制怎么选适合把 next-key lock、一致性读、复制延迟和分库分表边界放回同一条一致性主线上判断。MySQL 专题 · MySQL 事务、锁与高可用边界跨专题关联 · 同场景:方案选型分词器、Tokenizer 和 IK 到底怎么选适合把分词、复杂查询和深分页放到一起深入看。Elasticsearch 专题 · Elasticsearch 查询与分析细节
继续阅读设计模式与结构抽象当前序列第 2 篇 / 共 3 篇当前专题第 1 个序列 / 共 3 个序列
往前看
上一篇设计模式总览回到当前序列上一章上一序列Spring Boot 与事务实践从第 1 篇开始:Spring Boot 开发笔记
往后看
下一篇单例模式怎么写更稳继续当前序列下一章下一序列分布式系统设计基线从第 1 篇开始:接口幂等到底应该怎么设计

把零散经验整理成可查、可复用、可持续更新的企业级知识门户