Appearance
抽象工厂模式 (Abstract Factory Pattern)
一、定义
一句话概括:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
官方定义(GoF):Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
抽象工厂模式是工厂方法模式的升级版。工厂方法模式只生产一种产品,而抽象工厂模式生产一个产品族(一组相关产品)。它解决了"一系列相关对象"的创建问题,确保客户端使用的产品是相互兼容的。
二、解决的问题
2.1 什么场景下需要抽象工厂模式?
- 产品族概念:系统需要一系列相关的产品对象一起工作,且这些产品必须保持一致。
- 跨平台 UI 系统:同一套 UI 在不同操作系统上需要不同的外观(Windows 风格、Mac 风格、Linux 风格)。
- 多数据库支持:同一套 DAO 需要支持 MySQL、Oracle、PostgreSQL 等多种数据库。
- 主题切换:应用需要支持多套主题,每套主题包含按钮、文本框、下拉框等组件。
2.2 不用抽象工厂会有什么问题?
假设一个跨平台 UI 库,如果不使用抽象工厂:
java
// 问题代码:客户端需要判断平台,且容易创建不兼容的组件
if (platform.equals("Windows")) {
Button btn = new WindowsButton();
TextField tf = new WindowsTextField();
} else if (platform.equals("Mac")) {
Button btn = new MacButton();
TextField tf = new MacTextField(); // 繁琐且容易出错
}
// 更严重的问题:可能混用不同平台的组件
Button btn = new WindowsButton();
TextField tf = new MacTextField(); // 不兼容,但编译器不会报错!问题分析:
- 客户端需要知道所有平台的所有组件类名
- 容易创建不兼容的产品组合(Windows 按钮 + Mac 文本框)
- 新增平台时需要修改所有客户端代码
- 组件之间的一致性无法保证
三、结构
3.1 文字描述
抽象工厂模式包含五个角色:
- AbstractProduct(抽象产品):定义产品的接口,可以有多个(如 Button、TextField)
- ConcreteProduct(具体产品):实现抽象产品接口
- AbstractFactory(抽象工厂):声明创建产品族的方法,每个方法返回一个抽象产品
- ConcreteFactory(具体工厂):实现抽象工厂,创建特定产品族的所有产品
- Client(客户端):只依赖抽象工厂和抽象产品
3.2 产品族与产品等级结构
产品等级结构(纵向)
──────────────────
Button 等级: Button (接口)
┌──────┼──────┐
WindowsButton MacButton
TextField 等级: TextField (接口)
┌──────┼──────┐
WindowsTextField MacTextField
产品族(横向):
┌──────────────────────────────────┐
│ Windows 产品族:WindowsButton │
│ WindowsTextField │
├──────────────────────────────────┤
│ Mac 产品族: MacButton │
│ MacTextField │
└──────────────────────────────────┘3.3 ASCII 类图
┌─────────────────────────┐
│ <<interface>> │
│ GUIFactory │
├─────────────────────────┤
│ + createButton(): Button │
│ + createTextField(): Text│
└─────────────────────────┘
△
┌───────┴───────┐
│ │
┌───────────────┐ ┌───────────────┐
│WindowsFactory │ │ MacFactory │
├───────────────┤ ├───────────────┤
│+createButton()│ │+createButton()│
│ → WindowsButton│ │ → MacButton │
│+createTextF() │ │+createTextF() │
│ → WindowsTextF │ │ → MacTextF │
└───────────────┘ └───────────────┘
┌──────────┐ ┌──────────────┐
│ <<intf>> │ │ <<intf>> │
│ Button │ │ TextField │
├──────────┤ ├──────────────┤
│+click() │ │+input() │
└──────────┘ └──────────────┘
△ △
┌───┴───┐ ┌───┴───┐
│ │ │ │
┌─────┐ ┌────┐ ┌──────┐ ┌──────┐
│WinBtn│ │MacBtn│ │WinTF │ │MacTF │
└─────┘ └────┘ └──────┘ └──────┘四、代码实现
4.1 基础实现
java
/**
* 抽象工厂模式 —— 跨平台 UI 组件库
* 业务场景:同一套 UI 代码在不同操作系统上渲染不同风格
*/
public class AbstractFactoryDemo {
// ========== 1. 抽象产品:按钮 ==========
public interface Button {
void render();
void onClick(Runnable action);
}
// ========== 2. 抽象产品:文本框 ==========
public interface TextField {
void render();
void setText(String text);
String getText();
}
// ========== 3. 抽象产品:复选框 ==========
public interface Checkbox {
void render();
boolean isChecked();
void toggle();
}
// ========== 4. 抽象工厂 ==========
public interface GUIFactory {
Button createButton();
TextField createTextField();
Checkbox createCheckbox();
}
// ========== 5. Windows 具体产品 ==========
public static class WindowsButton implements Button {
@Override
public void render() {
System.out.println("┌──────────────┐");
System.out.println("│ [Windows 按钮] │");
System.out.println("└──────────────┘");
}
@Override
public void onClick(Runnable action) {
System.out.println("[Windows] 按钮被点击");
action.run();
}
}
public static class WindowsTextField implements TextField {
private String text = "";
@Override
public void render() {
System.out.println("┌──────────────────────┐");
System.out.println("│ [Windows 文本框] " + text);
System.out.println("└──────────────────────┘");
}
@Override
public void setText(String text) { this.text = text; }
@Override
public String getText() { return text; }
}
public static class WindowsCheckbox implements Checkbox {
private boolean checked = false;
@Override
public void render() {
System.out.println("[X] Windows 复选框 (" + (checked ? "选中" : "未选中") + ")");
}
@Override
public boolean isChecked() { return checked; }
@Override
public void toggle() { this.checked = !this.checked; }
}
// ========== 6. Mac 具体产品 ==========
public static class MacButton implements Button {
@Override
public void render() {
System.out.println("╭──────────────╮");
System.out.println("│ [ Mac 按钮 ] │");
System.out.println("╰──────────────╯");
}
@Override
public void onClick(Runnable action) {
System.out.println("[Mac] 按钮被点击");
action.run();
}
}
public static class MacTextField implements TextField {
private String text = "";
@Override
public void render() {
System.out.println("╭──────────────────────╮");
System.out.println("│ [Mac 文本框] " + text);
System.out.println("╰──────────────────────╯");
}
@Override
public void setText(String text) { this.text = text; }
@Override
public String getText() { return text; }
}
public static class MacCheckbox implements Checkbox {
private boolean checked = false;
@Override
public void render() {
System.out.println("◉ Mac 复选框 (" + (checked ? "选中" : "未选中") + ")");
}
@Override
public boolean isChecked() { return checked; }
@Override
public void toggle() { this.checked = !this.checked; }
}
// ========== 7. 具体工厂 ==========
public static class WindowsFactory implements GUIFactory {
@Override
public Button createButton() { return new WindowsButton(); }
@Override
public TextField createTextField() { return new WindowsTextField(); }
@Override
public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}
public static class MacFactory implements GUIFactory {
@Override
public Button createButton() { return new MacButton(); }
@Override
public TextField createTextField() { return new MacTextField(); }
@Override
public Checkbox createCheckbox() { return new MacCheckbox(); }
}
// ========== 8. 客户端:应用 ==========
public static class Application {
private final Button button;
private final TextField textField;
private final Checkbox checkbox;
// 客户端只依赖抽象工厂和抽象产品
public Application(GUIFactory factory) {
this.button = factory.createButton();
this.textField = factory.createTextField();
this.checkbox = factory.createCheckbox();
}
public void render() {
System.out.println("\n====== 渲染界面 ======");
button.render();
textField.render();
checkbox.render();
System.out.println("======================\n");
}
public void interactive() {
textField.setText("Hello, Abstract Factory!");
checkbox.toggle();
button.onClick(() -> System.out.println("执行业务逻辑..."));
}
}
// ========== 9. 测试 ==========
public static void main(String[] args) {
// 通过配置决定使用哪个工厂(实际项目中从配置文件读取)
String os = System.getProperty("os.name").toLowerCase();
GUIFactory factory = os.contains("win") ? new WindowsFactory() : new MacFactory();
Application app = new Application(factory);
app.render();
app.interactive();
}
}4.2 进阶实现
4.2.1 产品族概念详解
java
/**
* 多数据库支持 —— 抽象工厂经典应用
* 产品族:MySQL 产品族(MySQLConnection + MySQLCommand + MySQLReader)
* PostgreSQL 产品族(PgConnection + PgCommand + PgReader)
*/
public class DatabaseAbstractFactory {
// ========== 抽象产品 ==========
public interface Connection {
void connect();
void close();
}
public interface Command {
void execute(String sql);
ResultSet getResultSet();
}
public interface ResultSet {
boolean next();
String getString(int columnIndex);
int getInt(int columnIndex);
}
// ========== 抽象工厂 ==========
public interface DatabaseFactory {
Connection createConnection(String url, String user, String password);
Command createCommand(Connection connection);
ResultSet createResultSet();
}
// ========== MySQL 产品族 ==========
public static class MySQLConnection implements Connection {
private final String url;
public MySQLConnection(String url, String user, String pwd) {
this.url = url;
System.out.println("[MySQL] 建立连接: " + url);
}
@Override
public void connect() { System.out.println("[MySQL] 已连接"); }
@Override
public void close() { System.out.println("[MySQL] 连接已关闭"); }
}
public static class MySQLCommand implements Command {
private final Connection conn;
public MySQLCommand(Connection conn) { this.conn = conn; }
@Override
public void execute(String sql) {
System.out.println("[MySQL] 执行: " + sql);
}
@Override
public ResultSet getResultSet() { return new MySQLResultSet(); }
}
public static class MySQLResultSet implements ResultSet {
private int rowIndex = -1;
@Override
public boolean next() { return ++rowIndex < 3; }
@Override
public String getString(int col) { return "MySQL-Value-" + col; }
@Override
public int getInt(int col) { return col * 100; }
}
// ========== PostgreSQL 产品族 ==========
public static class PgConnection implements Connection {
public PgConnection(String url, String user, String pwd) {
System.out.println("[PostgreSQL] 建立连接: " + url);
}
@Override
public void connect() { System.out.println("[PostgreSQL] 已连接"); }
@Override
public void close() { System.out.println("[PostgreSQL] 连接已关闭"); }
}
public static class PgCommand implements Command {
private final Connection conn;
public PgCommand(Connection conn) { this.conn = conn; }
@Override
public void execute(String sql) {
System.out.println("[PostgreSQL] 执行: " + sql);
}
@Override
public ResultSet getResultSet() { return new PgResultSet(); }
}
public static class PgResultSet implements ResultSet {
private int rowIndex = -1;
@Override
public boolean next() { return ++rowIndex < 3; }
@Override
public String getString(int col) { return "PG-Value-" + col; }
@Override
public int getInt(int col) { return col * 200; }
}
// ========== 具体工厂 ==========
public static class MySQLFactory implements DatabaseFactory {
@Override
public Connection createConnection(String url, String user, String password) {
return new MySQLConnection(url, user, password);
}
@Override
public Command createCommand(Connection connection) {
return new MySQLCommand(connection);
}
@Override
public ResultSet createResultSet() {
return new MySQLResultSet();
}
}
public static class PostgreSQLFactory implements DatabaseFactory {
@Override
public Connection createConnection(String url, String user, String password) {
return new PgConnection(url, user, password);
}
@Override
public Command createCommand(Connection connection) {
return new PgCommand(connection);
}
@Override
public ResultSet createResultSet() {
return new PgResultSet();
}
}
// ========== 数据访问层(客户端) ==========
public static class UserDAO {
private final DatabaseFactory dbFactory;
public UserDAO(DatabaseFactory dbFactory) {
this.dbFactory = dbFactory;
}
public void queryUsers() {
Connection conn = dbFactory.createConnection("jdbc:db://localhost/mydb", "root", "123456");
Command cmd = dbFactory.createCommand(conn);
conn.connect();
cmd.execute("SELECT * FROM users");
ResultSet rs = cmd.getResultSet();
while (rs.next()) {
System.out.println(" User: " + rs.getString(1) + ", Age: " + rs.getInt(2));
}
conn.close();
}
}
public static void main(String[] args) {
// 切换数据库只需换一个工厂
DatabaseFactory factory = new MySQLFactory();
UserDAO dao = new UserDAO(factory);
dao.queryUsers();
System.out.println("\n--- 切换到 PostgreSQL ---\n");
factory = new PostgreSQLFactory();
dao = new UserDAO(factory);
dao.queryUsers();
}
}4.3 生产级实现(Spring Boot 场景)
java
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 生产级多租户数据源工厂 —— 抽象工厂 + Spring Boot
*
* 业务场景:SaaS 平台需要为每个租户提供独立的数据源
* 每个数据源包含:连接池、事务管理器、SQL 方言
*/
@Component
public class MultiTenantDataSourceFactory {
// ========== 抽象产品 ==========
public interface DataSource {
String getType();
Connection getConnection();
}
public interface TransactionManager {
void begin();
void commit();
void rollback();
}
public interface SqlDialect {
String buildPaginationSql(String sql, int offset, int limit);
String getSequenceNextVal(String sequenceName);
}
// ========== 抽象工厂 ==========
public interface TenantDatabaseFactory {
DataSource createDataSource(String tenantId);
TransactionManager createTransactionManager(DataSource ds);
SqlDialect createSqlDialect();
}
// ========== MySQL 产品族 ==========
public static class MySQLDataSource implements DataSource {
private final String tenantId;
public MySQLDataSource(String tenantId) {
this.tenantId = tenantId;
System.out.println("[MySQL] 为租户 " + tenantId + " 创建数据源");
}
@Override
public String getType() { return "MySQL"; }
@Override
public Connection getConnection() {
return new MySQLConnection("jdbc:mysql://tenant-" + tenantId + "/db", "", "");
}
}
public static class MySQLTransactionManager implements TransactionManager {
@Override
public void begin() { System.out.println("[MySQL TX] BEGIN"); }
@Override
public void commit() { System.out.println("[MySQL TX] COMMIT"); }
@Override
public void rollback() { System.out.println("[MySQL TX] ROLLBACK"); }
}
public static class MySQLDialect implements SqlDialect {
@Override
public String buildPaginationSql(String sql, int offset, int limit) {
return sql + " LIMIT " + offset + ", " + limit;
}
@Override
public String getSequenceNextVal(String sequenceName) {
return "SELECT AUTO_INCREMENT FROM information_schema.tables WHERE table_name='" + sequenceName + "'";
}
}
// ========== PostgreSQL 产品族 ==========
public static class PgDataSource implements DataSource {
private final String tenantId;
public PgDataSource(String tenantId) {
this.tenantId = tenantId;
System.out.println("[PG] 为租户 " + tenantId + " 创建数据源");
}
@Override
public String getType() { return "PostgreSQL"; }
@Override
public Connection getConnection() {
return new PgConnection("jdbc:postgresql://tenant-" + tenantId + "/db", "", "");
}
}
public static class PgTransactionManager implements TransactionManager {
@Override
public void begin() { System.out.println("[PG TX] BEGIN"); }
@Override
public void commit() { System.out.println("[PG TX] COMMIT"); }
@Override
public void rollback() { System.out.println("[PG TX] ROLLBACK"); }
}
public static class PgDialect implements SqlDialect {
@Override
public String buildPaginationSql(String sql, int offset, int limit) {
return sql + " OFFSET " + offset + " LIMIT " + limit;
}
@Override
public String getSequenceNextVal(String sequenceName) {
return "SELECT NEXTVAL('" + sequenceName + "')";
}
}
// ========== 具体工厂 ==========
public static class MySQLTenantFactory implements TenantDatabaseFactory {
@Override
public DataSource createDataSource(String tenantId) {
return new MySQLDataSource(tenantId);
}
@Override
public TransactionManager createTransactionManager(DataSource ds) {
return new MySQLTransactionManager();
}
@Override
public SqlDialect createSqlDialect() {
return new MySQLDialect();
}
}
public static class PgTenantFactory implements TenantDatabaseFactory {
@Override
public DataSource createDataSource(String tenantId) {
return new PgDataSource(tenantId);
}
@Override
public TransactionManager createTransactionManager(DataSource ds) {
return new PgTransactionManager();
}
@Override
public SqlDialect createSqlDialect() {
return new PgDialect();
}
}
// ========== 租户数据源注册中心 ==========
private final Map<String, TenantDatabaseFactory> factoryRegistry = new ConcurrentHashMap<>();
public MultiTenantDataSourceFactory() {
// 注册数据库工厂
factoryRegistry.put("mysql", new MySQLTenantFactory());
factoryRegistry.put("postgresql", new PgTenantFactory());
}
/**
* 为租户创建完整的数据访问环境
*/
public TenantDatabaseContext createTenantContext(String tenantId, String dbType) {
TenantDatabaseFactory factory = factoryRegistry.get(dbType.toLowerCase());
if (factory == null) {
throw new IllegalArgumentException("不支持的数据库类型: " + dbType);
}
DataSource ds = factory.createDataSource(tenantId);
TransactionManager tm = factory.createTransactionManager(ds);
SqlDialect dialect = factory.createSqlDialect();
return new TenantDatabaseContext(tenantId, ds, tm, dialect);
}
public static class TenantDatabaseContext {
public final String tenantId;
public final DataSource dataSource;
public final TransactionManager transactionManager;
public final SqlDialect sqlDialect;
public TenantDatabaseContext(String tenantId, DataSource ds, TransactionManager tm, SqlDialect dialect) {
this.tenantId = tenantId;
this.dataSource = ds;
this.transactionManager = tm;
this.sqlDialect = dialect;
}
public void executeInTransaction(Runnable businessLogic) {
transactionManager.begin();
try {
businessLogic.run();
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollback();
throw e;
}
}
}
// ========== 测试 ==========
public static void main(String[] args) {
MultiTenantDataSourceFactory registry = new MultiTenantDataSourceFactory();
TenantDatabaseContext tenant1 = registry.createTenantContext("T001", "mysql");
TenantDatabaseContext tenant2 = registry.createTenantContext("T002", "postgresql");
System.out.println("\n租户 T001 SQL: " +
tenant1.sqlDialect.buildPaginationSql("SELECT * FROM orders", 0, 10));
System.out.println("租户 T002 SQL: " +
tenant2.sqlDialect.buildPaginationSql("SELECT * FROM orders", 0, 10));
tenant1.executeInTransaction(() ->
System.out.println("[T001] 执行业务逻辑..."));
}
}五、优缺点
优点
| 优点 | 说明 |
|---|---|
| 保证产品一致性 | 同一工厂创建的产品是兼容的,不会出现 Windows 按钮 + Mac 文本框 |
| 符合开闭原则 | 新增产品族只需添加新的工厂类,无需修改已有代码 |
| 符合单一职责原则 | 产品创建逻辑集中在工厂中 |
| 隔离具体类 | 客户端只依赖抽象接口,不依赖具体实现 |
缺点
| 缺点 | 说明 |
|---|---|
| 扩展产品等级结构困难 | 新增一个抽象产品(如新增"下拉框"),需要修改所有工厂类 |
| 类数量多 | 每个产品族需要一个工厂,每个产品等级需要多个产品类 |
| 增加系统复杂度 | 引入了大量接口和抽象类 |
六、适用场景
- 跨平台 UI 框架:如 Qt、Swing LookAndFeel、Flutter 的 Material/Cupertino
- 多数据库支持:同一套代码适配 MySQL、Oracle、PostgreSQL
- 多主题系统:深色主题/浅色主题的组件切换
- 多租户 SaaS 系统:不同租户使用不同数据库或不同配置
- 序列化/反序列化框架:JSON、XML、Protobuf 等多种格式转换
- 游戏开发:不同关卡/地图创建不同的敌人、道具、障碍物
- 中间件适配:同一套代码适配不同消息队列(Kafka、RabbitMQ、RocketMQ)
七、JDK / Spring 框架中的实际应用
7.1 JDK 中的抽象工厂
1. javax.xml.parsers.DocumentBuilderFactory
- 创建与实现无关的 XML 解析器(DOM 解析器)
- DocumentBuilderFactory.newInstance() 根据配置返回具体工厂
2. javax.xml.transform.TransformerFactory
- 创建 XSLT 转换器,支持不同实现
3. javax.xml.xpath.XPathFactory
- 创建 XPath 表达式解析器
4. java.sql.DriverManager
- 管理不同数据库驱动,虽然形式不同但思想类似
5. java.awt.Toolkit
- 创建与平台相关的 GUI 组件7.2 Spring 框架中的抽象工厂
1. AbstractApplicationContext
- 整个 Spring IoC 容器就是抽象工厂的体现
- getBean() 方法返回不同类型的 Bean
2. AbstractBeanFactory
- Spring 内部使用,支持不同 Bean 作用域
3. FactoryBean<T>
- 工厂方法 + 抽象工厂的结合体
4. PlatformTransactionManager
- 抽象事务管理器,JdbcTransactionManager、JtaTransactionManager 等为具体实现八、与其他模式的关系
| 相关模式 | 关系说明 |
|---|---|
| 工厂方法模式 | 抽象工厂通常用工厂方法实现每个产品的创建。工厂方法只有一种产品,抽象工厂有产品族 |
| 单例模式 | 具体工厂类通常被设计为单例,整个应用只需要一个工厂实例 |
| 原型模式 | 抽象工厂可以用原型模式替代,将工厂方法改为克隆已有的原型对象 |
| 建造者模式 | 建造者专注于一步步构建复杂对象,抽象工厂专注于创建产品族 |
| 桥接模式 | 桥接模式分离抽象和实现,抽象工厂创建产品族,两者可以组合使用 |
九、面试常见问题
Q1:抽象工厂和工厂方法的区别?
答案:
| 对比维度 | 工厂方法 | 抽象工厂 |
|---|---|---|
| 产品数量 | 只创建一种产品 | 创建多种产品(产品族) |
| 工厂数量 | 一个工厂对应一个产品 | 一个工厂对应多个产品 |
| 抽象层次 | 工厂和产品都是抽象的 | 工厂更抽象,封装了产品族 |
一句话总结:工厂方法是一个工厂生产一种产品,抽象工厂是一个工厂生产一系列相关产品。
Q2:抽象工厂如何保证产品族的一致性?
答案: 抽象工厂模式通过将产品族的创建封装在同一个工厂中来保证一致性。客户端通过同一个工厂实例创建所有产品,因此不会出现混用不同产品族的情况。例如,WindowsFactory 创建的所有产品都是 Windows 风格的,不会出现 Mac 风格的组件。
Q3:抽象工厂模式的最大缺点是什么?如何解决?
答案: 最大缺点是扩展产品等级结构困难。如果要新增一个抽象产品(如新增"下拉框"组件),需要修改抽象工厂接口和所有具体工厂类。
解决方案:
- 为每个工厂方法提供默认实现(Java 8 接口默认方法)
- 使用反射或配置驱动,减少硬编码
- 在 Spring 中,利用
List<Interface>自动注入替代传统抽象工厂
Q4:Spring 中如何体现抽象工厂模式?
答案: Spring 的 ApplicationContext 就是抽象工厂的体现:
getBean(String name)返回不同类型的 Bean → 类似工厂方法- 可以一次性获取多个相关的 Bean → 类似抽象工厂
AbstractApplicationContext的各种实现(XmlWebApplicationContext、AnnotationConfigApplicationContext)→ 具体工厂
Q5:什么时候应该使用抽象工厂而不是工厂方法?
答案: 当系统中存在多个产品族,且这些产品族之间需要保持一致时,使用抽象工厂。例如:
- 需要同时创建 Button、TextField、Checkbox,且它们必须属于同一平台
- 需要同时创建 Connection、Command、ResultSet,且它们必须属于同一数据库
如果只有一种产品需要创建,工厂方法就足够了。
总结
抽象工厂模式是工厂方法模式的升级版,核心在于产品族的概念。它确保同一工厂创建的产品是兼容的,特别适合需要跨平台、多数据库、多主题等场景。
实际开发中:
- 简单场景:工厂方法足够
- 产品族场景:使用抽象工厂
- Spring 项目:利用
ApplicationContext和自动注入机制,可以优雅地替代传统抽象工厂 - 新增产品困难时,可考虑用原型模式配合使用
