抽象工厂模式
从披萨原料工厂说起
披萨连锁店做大了,需要保证每家门店用的原料都"正宗"——纽约的薄脆面团、新鲜蛤蜊;芝加哥的厚脆面团、冷冻蛤蜊。问题是,如果每种 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 配置,是抽象工厂思想的框架级实现。