这是一个非常经典的问题。

很多朋友分不清这三个,是因为它们都在干同一件事:把 new 关键字隐藏起来。但是它们的复杂度解决的问题边界不同。

我们可以把它们看作是工厂发展的三个阶段:从“小作坊”到“大工厂”再到“产业生态链”。


先说结论:

  • 简单工厂(静态工厂)一个工厂类(通常用静态方法)包揽所有同类产品的创建,无抽象工厂接口,产品可实现同一接口。(非 GOF 23 种设计模式,是基础封装)
    • 基础封装,一个工厂对应多个产品通常只有一个相关工厂
  • 工厂方法:定义抽象工厂接口(仅负责创建「单一产品」),每个具体产品对应一个具体工厂,工厂方法延迟到具体工厂实现。
    • 工厂方法模式本质就是对获取对象过程的抽象
  • 抽象工厂定义抽象工厂接口(负责创建「一系列相关 / 依赖的产品(产品族)」),每个具体工厂对应一个产品族,负责创建该产品族的所有产品
    • 一个工厂对应多个产品
    • 有多个不同品牌的工厂,所以需要一个抽象工厂
      • 抽象工厂定义多个产品的抽象生产方法
      • 具体工厂定义多个产品的具体生产方法
    • 由于产品有多个品牌(一个品牌对应一个工厂),所以会有抽象产品
    • 对应有多个“品牌”(mysql,oracle),有多种“产品”的场景(事务管理者,事务状态等)

一、 三种工厂模式的对比与区别

简单工厂 (Simple Factory) —— 其实不是标准设计模式,而是一种编程习惯
  • 别名:静态工厂方法 (Static Factory Method)。
  • 核心逻辑一个工厂类,一个静态方法,一堆 if-else

将新建对象的逻辑放置在工厂中(里层逻辑可以内置在类中)

当然,如果出现了创建对象逻辑相当复杂的情况,可以使用工厂方法模式,创建工厂,在工厂中封装复杂逻辑(工厂方法模式本质就是对获取对象过程的抽象

通常配合策略模式一起使用,把具体逻辑放到策略类中,客户通过简单工厂取出策略,调用被实现的抽象方法。

为了实现可扩展性,可以使用 枚举 + Map + 提供获取方法 的方式,避免大堆 if-else,见策略模式

简单工厂和工厂方法其实是不互斥的,可以将复杂的创建逻辑封装到工厂方法中,然后将 new 对象的选择过程放在简单工厂模式中(选择调用不同工厂的 getInstance 方法创建不同的产品对象)。
如果不用 spring 的情况下,简单工厂可能会耦合,但是工厂方法不会耦合。使用工厂方法的情况下,如果有了新的产品,新增一个新的工厂类即可(新增)。如果工厂方法还整合了简单工厂,那么新增产品时还需要对简单工厂进行修改,这违反了开闭原则。
不过如果是 spring 的话,策略模式笔记中演示的那种方式已经实现解耦了。

甚至可以配合反射 + 配置文件使用,用指定类名的方式来创建对象

img

  • 类比自动售货机。你塞进去钱,按下按钮(参数),它吐出你要的可乐或雪碧(产品)。你不需要知道可乐是怎么灌装的。
  • 代码模型
public class PhoneFactory {
    public static Phone createPhone(String type) {
        if ("iphone".equals(type)) return new IPhone();
        else if ("huawei".equals(type)) return new HuaweiPhone();
        else if("xiaomi".equals(type)) return new XiaomiPhone();
        return null;
    }
}
  • 缺点违背开闭原则 (OCP)。如果要卖小米手机,你必须修改 PhoneFactory 的源代码(加个 else if)。
不适用场景
  1. 产品需要频繁新增(比如每月都要新增 1-2 种订单类型),每次新增都要修改工厂类的 if/else 逻辑,违反「开闭原则」,维护成本越来越高。
  2. 产品创建逻辑复杂且差异极大(比如每种订单的创建逻辑都完全不同,无共性可提取),工厂类会变得臃肿,难以维护。
  3. 对系统的「可扩展性」「可维护性」要求极高(如大型分布式系统的核心模块)。
工厂方法 (Factory Method) —— 标准的 GoF 模式

若每个产品的创建逻辑相对独立或困难(差异较大,或难以通过简单 if/else 封装)时,此时不宜仅仅采用简单工厂模式,则需要工厂方法单独封装。

由于提供了统一的创造接口,所以工厂方法解耦是相对轻松的。

  • 核心逻辑把“生产”推迟到子类。 定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法模式本质就是对获取对象过程的抽象
  • 生活类比专卖店体系
    • 想要苹果手机?去“苹果手机工厂店”(只生产苹果)。
    • 想要华为手机?去“华为手机工厂店”(只生产华为)。
    • 总公司只制定“生产标准”,不负责具体生产。
  • 代码模型
// 抽象工厂接口
// Phone 统一通过这个接口获取,日后想增加具体手机只需增加实现类,可扩展性大
public interface PhoneFactory {
    Phone createPhone();
}

// 具体工厂 A
public class IPhoneFactory implements PhoneFactory {
    // 一大堆复杂逻辑
    public Phone createPhone() { return new IPhone(); }
}

// 具体工厂 B
public class HuaweiFactory implements PhoneFactory {
    // 一大堆复杂逻辑
    public Phone createPhone() { return new HuaweiPhone(); }
}
  • 优点符合****开闭原则。要增加小米手机,新建一个 XiaomiFactory 即可,不用改原有代码。
  • 缺点类爆炸。每增加一种产品,就要增加一个对应的工厂类(因为工厂方法中的工厂是对类创建过程的封装)。
不适用场景
  1. 产品种类极少且固定(比如只有 2 种产品),用工厂方法会增加类的数量(额外的抽象工厂、具体工厂),过度设计,不如简单工厂简洁。
  2. 产品之间存在依赖关系(比如创建「手机」的同时,需要创建「手机充电器」「手机耳机」,三者是一套产品),工厂方法只关注单一产品,无法保证产品之间的兼容性。
  3. 对「类的数量」敏感(如嵌入式系统、轻量级应用),产品增多时会出现「工厂爆炸」(一个产品对应一个工厂),增加维护成本。
抽象工厂 (Abstract Factory) —— 最复杂,用于“产品族”

需要创建「一系列相关 / 依赖的产品(产品族)」、保证产品族内的兼容性、扩展产品族而非单一产品,追求「产品族的整体封装与兼容」

产品族 vs 产品等级结构
概念示例(电子设备产品)
产品等级结构(单一产品接口)手机、充电器、耳机(每个都是独立的产品等级,有各自的抽象接口)
产品族(一系列相关产品)华为产品族(华为手机、华为充电器、华为耳机)、苹果产品族(苹果手机、苹果充电器、苹果耳机)
抽象工厂的核心创建「产品族」,而不是单一产品等级

核心关键点:

  1. 产品属于「多个相关的产品等级结构」(即「产品族」),比如「手机」「充电器」「耳机」是三个产品等级,「华为产品族」包含华为手机、华为充电器、华为耳机,三者相互依赖、兼容。
  2. 客户端需要的是「一套产品」,而不是单一产品,且需要保证「同一产品族的产品能相互配合」。
  3. 优先考虑「产品族的扩展性」,接受「单一产品扩展困难」的代价。

优势:① 保证产品族兼容性:HuaWeiFactory 创建的产品必然是华为系列,可无缝交互,避免出现「华为手机搭配苹果平板」的不兼容场景;② 扩展产品族容易:新增「小米产品族」时,只需新增 XiaoMiFactory 和对应的三个产品类,无侵入性。③ 切换品牌时,只需更换具体工厂,即可获取该工厂的全套产品,无需逐个修改产品,避免产品不配套导致的问题(这里类比切换环境,产品类比配置,更好理解) ④ 有的时候还能做到同一品牌产品族之间的联动(类比不同职业的装备)

在《大话设计模式》一书中,抽象工厂最终还是被改成了简单工厂模式。在简单的逻辑中,抽象工厂不如简单工厂,直接把创建具体具体产品的类放到简单工厂中

img

原来的逻辑:

img

  • 核心逻辑生产“全家桶”。它创建的不是一个对象,而是一组相关的对象(产品族)。

  • 核心部分:

    • 工厂接口(由具体品牌实现,定义创造抽象产品的方法: 定义创建「事务管理器」、「事务状态」、「事务连接」的方法)
    • 具体工厂 (返回具体产品,创建 JDBC 产品族、创建 Hibernate 产品族)
    • 工厂的意义是创造
    • 产品接口(品牌产品实现,定义产品的功能:事务管理器可以干嘛,事务连接可以干嘛,事务状态可以干嘛,由于数据库的事务操作都是通用的,而具体逻辑不同,所以要有产品接口这一层产品的抽象)
    • 具体产品(被返回的对象,在方法里面实现各个产品的具体逻辑)
  • 生活类比数码套装

    • 苹果工厂:生产 {iPhone, iPad, Mac} —— 这是一整套,风格统一,接口兼容。
    • 华为工厂:生产 {Mate, MatePad, MateBook} —— 也是一整套。
    • 你不能让苹果工厂生产 MateBook。
  • 代码模型

// 工厂接口,定义创造抽象产品的方法
public interface ElectronicsFactory {
    Phone createPhone();
    Computer createComputer();
}

// 苹果全家桶工厂
public class AppleFactory implements ElectronicsFactory {
    // 对的,Phone 也是个接口
    // 如果这里是华为工厂,那么 createPhone 生产出来的就是华为手机
    public Phone createPhone() { return new IPhone(); }
    public Computer createComputer() { return new Mac(); }
}

// 使用,指定要那个工厂生产的东西,除了 new 的工厂不一样之外,其他都是通用
// 后续要增加新的电子产品时:工厂接口和工厂需要加上对应方法
// 抽象产品增加一个,具体产品增加品牌数个
ElectronicsFactory fac1 = new AppleFacrory();
Phone applePhone = fac1.createPhone();
applePhone.call();    // 打电话:信号不好


ElectronicsFactory fac2 = new XiaomiFactory();
Phonw xiaomiPhone = fac2.createPhone();
xiaomiPhone,call();   // 打电话:接通率了
  • 应用场景:需要保证客户端只使用同一个产品族的对象。比如:换肤功能(这套皮肤的 按钮、滚动条、窗口背景 必须风格统一);跨数据库操作(Connection, Statement, ResultSet 必须都是 MySQL 的,或者都是 Oracle 的)。
不适用场景
  1. 只需要创建单一产品(比如只需要创建手机,不需要平板、电脑),用抽象工厂会过度设计,增加不必要的接口和类,不如工厂方法简洁。
  2. 需要频繁扩展单一产品等级结构(比如在「手机、平板、电脑」的产品族中,频繁新增其他电子产品),此时需要修改抽象工厂接口,违反「开闭原则」,所有具体工厂都需要修改,维护成本极高。
  3. 产品之间无依赖关系(比如手机和充电器无需配套,可随意搭配),无需保证兼容性,用抽象工厂会增加复杂度,不如工厂方法灵活。

扩展阅读:

PlatformTransactionManager 是 Spring 事务管理的顶层接口,也是抽象工厂模式的应用,核心拆解:

  • 抽象工厂接口:PlatformTransactionManager(定义了创建「事务产品族」的方法,getTransaction() 创建事务状态、commit() 提交事务、rollback() 回滚事务)。
  • 具体工厂类:DataSourceTransactionManager(JDBC/MyBatis 事务工厂,创建「JDBC 事务产品族」)、HibernateTransactionManager(Hibernate 事务工厂,创建「Hibernate 事务产品族」)。
  • 产品族:「JDBC 事务产品族」(包含 DataSourceTransactionManagerTransactionStatusConnection)、「Hibernate 事务产品族」(包含 HibernateTransactionManagerTransactionStatusSession)。
  • 抽象工厂的体现:每个具体工厂(DataSourceTransactionManager)创建一个「事务产品族」,保证事务相关产品的兼容性,扩展新的事务类型(如 Redis 事务)只需新增 PlatformTransactionManager 实现类

二、 总结对比表

特性简单工厂工厂方法抽象工厂
创建对象数量一个一个一系列 (产品族)
复杂度
开闭原则违背 (需改代码)符合 (只增类)倾斜 (加产品族容易,加新产品难)
核心特点用于创建逻辑简单的对象用于解耦创建与使用用于保证产品组合的一致性
常见程度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

三、 谁是“策略模式”的最佳拍档?

答案是:简单工厂 (Simple Factory) 或 它的进化版(Map 注册表)

为什么?
  1. 策略模式 负责定义“怎么做”(算法/行为)。
  2. 工厂模式 负责解决“用哪个”(创建/选择)。

在实际业务中,我们通常需要根据前端传来的 type(比如 “ALI_PAY”)来找到对应的策略。这正是简单工厂最擅长的场景:根据参数返回实例

实战中的“混合体”

在 Spring 中,我们很少写原始的 if-else 简单工厂,而是使用 “Map 注入 + 简单工厂” 的方式(就是策略模式笔记中的 PaymentFactory)。

  • 它的外壳:看起来像工厂(提供 getStrategy(type) 方法)。
  • 它的内核:利用了 Spring 的自动注册机制,既避免了 if-else(简单工厂的缺点),又避免了写无数个工厂类(工厂方法的缺点)。

结论:在 Spring 环境下,策略模式 + (Map版) 简单工厂 是消除 if-else 的终极杀招。

四、 什么时候用抽象工厂?

你在做业务系统(CRUD)时,99% 的情况用不到抽象工厂

抽象工厂通常出现在 中间件开发跨平台框架 中:

  1. JDBC:这是典型的抽象工厂。当你连接 MySQL 时,Connection、Statement、ResultSet 这一整套都是 MySQL 驱动提供的;当你换成 Oracle 时,这一整套都要换成 Oracle 的实现。你不能用 MySQL 的 Connection 去创建 Oracle 的 Statement。
  2. UI 库 (AWT/Swing):在 Windows 下,按钮和窗口是 Windows 风格的;在 Linux 下,是 GTK/KDE 风格的。工厂负责生产这一整套 UI 组件。

总结

  • 做业务:用 简单工厂(配合 Map)来管理策略或 Bean。
  • 写框架/库:如果你的框架需要支持多套底层实现(如支持多种数据库、多种操作系统),才考虑 抽象工厂
  • 一般解耦:如果仅仅是为了把 new 拿出去,用 工厂方法

更多推荐