跳转至

抽象工厂模式

从披萨原料工厂说起

披萨连锁店做大了,需要保证每家门店用的原料都"正宗"——纽约的薄脆面团、新鲜蛤蜊;芝加哥的厚脆面团、冷冻蛤蜊。问题是,如果每种 Pizza 直接 new NYDough()new NYSauce(),那么当你想在加州开店时,就要把所有 Pizza 类都改一遍。

解决方案: 建立"原料工厂"接口 PizzaIngredientFactory,纽约工厂和芝加哥工厂各自提供一整套原料。Pizza 只面向接口,不依赖任何具体原料类。

🔍 定义

抽象工厂模式(Abstract Factory)提供一个接口,用于创建一系列相关或相互依赖的对象(产品族),而无需指定具体类。

工厂方法关注"创建一种产品的哪个变体",抽象工厂关注"创建一整套配套产品,保证它们来自同一族"。

⚠️ 不使用该模式存在的问题

每个城市的门店直接 new 具体原料,无法保证原料的一致性:

AbstractFactoryBadExample.java
package com.example.creational.abstract_factory;

/**
 * 抽象工厂模式 - 反例
 * 问题:直接 new 具体组件,切换 UI 平台需要修改大量代码
 */
public class AbstractFactoryBadExample {
    public static void main(String[] args) {
        // ❌ 硬编码了具体平台,切换到 Mac 要改所有 new 语句
        ApplicationBad app = new ApplicationBad("windows");
        app.render();
    }
}

// ❌ Application 直接 new 具体组件
class ApplicationBad {
    private final String platform;

    public ApplicationBad(String platform) {
        this.platform = platform;
    }

    public void render() {
        ButtonBad  button;
        InputBad   input;
        DialogBad  dialog;

        if ("windows".equals(platform)) {
            button = new WindowsButton();
            input  = new WindowsInput();
            dialog = new WindowsDialog();
        } else if ("mac".equals(platform)) {
            button = new MacButton();        // ❌ 每次都要在这里判断
            input  = new MacInput();
            dialog = new MacDialog();
        } else {
            throw new IllegalArgumentException("未知平台:" + platform);
        }

        button.render();
        input.render();
        dialog.render();
    }
}

// 抽象 UI 组件
interface ButtonBad { void render(); }
interface InputBad  { void render(); }
interface DialogBad { void render(); }

// Windows 组件
class WindowsButton implements ButtonBad { public void render() { System.out.println("[Windows] 按钮"); } }
class WindowsInput  implements InputBad  { public void render() { System.out.println("[Windows] 输入框"); } }
class WindowsDialog implements DialogBad { public void render() { System.out.println("[Windows] 对话框"); } }

// Mac 组件
class MacButton implements ButtonBad { public void render() { System.out.println("[Mac] 按钮"); } }
class MacInput  implements InputBad  { public void render() { System.out.println("[Mac] 输入框"); } }
class MacDialog implements DialogBad { public void render() { System.out.println("[Mac] 对话框"); } }

🏗️ 设计模式结构(披萨原料工厂)

%%{init: {'themeVariables': {'noteBkgColor': 'transparent', 'noteBorderColor': '#768390'}}}%%
classDiagram
    classDef default fill:transparent,stroke:#768390
    class PizzaIngredientFactory {
        <<interface>>
        +createDough() Dough
        +createSauce() Sauce
        +createCheese() Cheese
        +createClams() Clams
    }
    class NYPizzaIngredientFactory {
        +createDough() ThinCrustDough
        +createSauce() MarinaraSauce
        +createCheese() ReggianoCheese
        +createClams() FreshClams
    }
    class ChicagoPizzaIngredientFactory {
        +createDough() ThickCrustDough
        +createSauce() PlumTomatoSauce
        +createCheese() MozzarellaCheese
        +createClams() FrozenClams
    }
    class AFPizza {
        <<abstract>>
        #dough: Dough
        #sauce: Sauce
        #cheese: Cheese
        +prepare() void*
    }
    class AFCheesePizza {
        -ingredientFactory: PizzaIngredientFactory
        +prepare() void
    }
    PizzaIngredientFactory <|.. NYPizzaIngredientFactory
    PizzaIngredientFactory <|.. ChicagoPizzaIngredientFactory
    AFPizza <|-- AFCheesePizza
    AFCheesePizza o--> PizzaIngredientFactory
    note for PizzaIngredientFactory "抽象工厂(AbstractFactory)"
    note for NYPizzaIngredientFactory "具体工厂(ConcreteFactory)"
    note for AFPizza "抽象产品(AbstractProduct)"
    note for AFCheesePizza "具体产品(ConcreteProduct)"

核心角色:

角色 说明
PizzaIngredientFactory(抽象工厂) 声明创建原料族的方法
NYPizzaIngredientFactory(具体工厂) 生产纽约风格的一套原料
Dough/Sauce/Cheese(抽象产品) 各类原料的接口
ThinCrustDough(具体产品) 纽约风格的薄脆面团

💻 设计模式举例说明

AbstractFactoryExample.java
package com.example.creational.abstract_factory;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 抽象工厂模式 - 正例
 * 工厂族保证同一平台的组件总是一起创建,切换平台只需换一个工厂实例
 */
public class AbstractFactoryExample {
    public static void main(String[] args) {
        // ✅ 只需切换工厂,所有 DAO 自动换成对应数据库的实现
        runApp(new MySQLDaoFactory());
        System.out.println("---");
        runApp(new H2DaoFactory());
    }

    static void runApp(DaoFactory factory) {
        OrderApplicationService service = new OrderApplicationService(factory);
        service.createAndQueryOrder();
    }
}

// 领域模型
class User {
    private final Long   id;
    private final String name;

    public User(Long id, String name) { this.id = id; this.name = name; }

    public Long   getId()   { return id;   }
    public String getName() { return name; }
}

class Order {
    private final Long   id;
    private final Long   userId;
    private final String productId;
    private       String status;

    public Order(Long id, Long userId, String productId) {
        this.id        = id;
        this.userId    = userId;
        this.productId = productId;
        this.status    = "PENDING";
    }

    public Long   getId()        { return id;        }
    public Long   getUserId()    { return userId;    }
    public String getProductId() { return productId; }
    public String getStatus()    { return status;    }
    public void   setStatus(String status) { this.status = status; }
}

// DAO 接口
interface UserDao {
    void   save(User user);
    User   findById(Long id);
    List<User> findAll();
}

interface OrderDao {
    void    save(Order order);
    Order   findById(Long id);
    List<Order> findByUserId(Long userId);
    void    updateStatus(Long orderId, String status);
}

// MySQL 实现
class MySQLUserDao implements UserDao {
    private final List<User> store = new ArrayList<>();

    @Override public void save(User user) { store.add(user); System.out.println("[MySQL] 保存用户: " + user.getName()); }
    @Override public User findById(Long id) { return store.stream().filter(u -> u.getId().equals(id)).findFirst().orElse(null); }
    @Override public List<User> findAll() { return store; }
}

class MySQLOrderDao implements OrderDao {
    private final List<Order> store = new ArrayList<>();

    @Override public void save(Order order) { store.add(order); System.out.println("[MySQL] 保存订单: " + order.getId()); }
    @Override public Order findById(Long id) { return store.stream().filter(o -> o.getId().equals(id)).findFirst().orElse(null); }
    @Override public List<Order> findByUserId(Long userId) { return store.stream().filter(o -> o.getUserId().equals(userId)).toList(); }
    @Override public void updateStatus(Long orderId, String status) { findById(orderId).setStatus(status); }
}

// H2 实现(内存)
class H2UserDao implements UserDao {
    private final List<User> store = new ArrayList<>();

    @Override public void save(User user) { store.add(user); System.out.println("[H2] 保存用户: " + user.getName()); }
    @Override public User findById(Long id) { return store.stream().filter(u -> u.getId().equals(id)).findFirst().orElse(null); }
    @Override public List<User> findAll() { return store; }
}

class H2OrderDao implements OrderDao {
    private final List<Order> store = new ArrayList<>();

    @Override public void save(Order order) { store.add(order); System.out.println("[H2] 保存订单: " + order.getId()); }
    @Override public Order findById(Long id) { return store.stream().filter(o -> o.getId().equals(id)).findFirst().orElse(null); }
    @Override public List<Order> findByUserId(Long userId) { return store.stream().filter(o -> o.getUserId().equals(userId)).toList(); }
    @Override public void updateStatus(Long orderId, String status) { findById(orderId).setStatus(status); }
}

// 抽象工厂:创建一族相关 DAO
interface DaoFactory {
    UserDao  createUserDao();
    OrderDao createOrderDao();
}

// 具体工厂:MySQL
class MySQLDaoFactory implements DaoFactory {
    @Override public UserDao  createUserDao()  { return new MySQLUserDao();  }
    @Override public OrderDao createOrderDao() { return new MySQLOrderDao(); }
}

// 具体工厂:H2(用于测试)
class H2DaoFactory implements DaoFactory {
    @Override public UserDao  createUserDao()  { return new H2UserDao();  }
    @Override public OrderDao createOrderDao() { return new H2OrderDao(); }
}

// 应用服务:只依赖抽象工厂,不依赖任何具体数据库
class OrderApplicationService {
    private final UserDao  userDao;
    private final OrderDao orderDao;

    public OrderApplicationService(DaoFactory factory) {
        this.userDao  = factory.createUserDao();
        this.orderDao = factory.createOrderDao();
    }

    public void createAndQueryOrder() {
        User  user  = new User(1L, "张三");
        Order order = new Order(100L, 1L, "product-" + UUID.randomUUID().toString().substring(0, 8));

        userDao.save(user);
        orderDao.save(order);
        orderDao.updateStatus(100L, "PAID");

        Order found = orderDao.findById(100L);
        System.out.println("查询订单状态: " + found.getStatus()); // PAID
    }
}

工厂方法 vs 抽象工厂

维度 工厂方法 抽象工厂
关注点 创建**一种**产品 创建**一族**产品
扩展方式 新增产品类 + 工厂子类 新增整套产品类 + 工厂类
新增产品类型 ✅ 不修改已有代码 ❌ 所有工厂都要加方法
适用场景 产品类型会增长 产品族固定,族内实现会变化

⚖️ 优缺点

优点:

  • 产品族一致性:工厂保证一次性创建的对象都来自同一族,不会出现"纽约面团+芝加哥酱料"的混用
  • 切换方便:替换整套实现只需换一个工厂实例,业务代码无需修改
  • 符合**开闭原则**:新增城市(新增工厂实现)不改现有代码

缺点:

  • 难以新增产品种类:在工厂接口中新增一种原料(如 createMushroom()),所有已有工厂类都必须修改
  • 类数量较多:M 个工厂 × N 种产品 = M×N 个具体类

🔗 与其它模式的关系

相关模式 关系说明
工厂方法 抽象工厂在实现时通常包含多个工厂方法
单例 具体工厂通常实现为单例,整个应用只需一个工厂实例
原型 工厂内部保存原型,通过克隆来创建产品(另一种实现方式)

🗂️ 应用场景

  • 跨数据库 DAO 层(MySQL/H2/Oracle,按环境切换整套实现)
  • 跨平台 UI 框架(Windows/macOS 各一套风格统一的组件)
  • 测试与生产隔离(测试用内存实现,生产用真实实现)

🏭 工业视角

抽象工厂解决的是"产品族一致性"问题

抽象工厂与工厂方法的核心区别在于粒度:工厂方法创建**一种产品**,抽象工厂创建**一组相关产品**(产品族),并保证它们来自同一家族、可以协同工作。

典型场景:系统需要同时支持 MySQL 和 PostgreSQL,不只是 Connection 要切换,Statement、ResultSet 的处理方式也要配套切换。此时用抽象工厂,一次切换工厂,整套实现全部替换。

抽象工厂:一次切换,整套产品族替换
// 每个工厂提供配套的一组对象,保证产品族内部一致
public interface IConfigParserFactory {
    IRuleConfigParser createRuleParser();   // 规则配置解析器
    ISystemConfigParser createSystemParser(); // 系统配置解析器
}

public class JsonConfigParserFactory implements IConfigParserFactory {
    @Override
    public IRuleConfigParser createRuleParser() {
        return new JsonRuleConfigParser();
    }
    @Override
    public ISystemConfigParser createSystemParser() {
        return new JsonSystemConfigParser();
    }
}

Spring IoC 容器是工厂模式的终极形态

Spring 的 ApplicationContext 本质上是一个超级工厂:它读取配置(XML / 注解),动态决定创建哪些对象、如何注入依赖,并管理对象的生命周期。

这正是工厂模式核心价值的极致体现——将对象的创建与使用完全解耦。调用方只需声明"我需要一个 UserService",不关心它是如何被创建的、依赖了哪些其他 Bean。

实际工程中的抽象工厂

在日常业务开发中,抽象工厂直接出现的频率不高。但"切换整套实现"的需求(多数据源、多环境、测试替身)都是它在解决的问题。 Spring Profiles + 条件 Bean 配置,是抽象工厂思想的框架级实现。