抽象工厂模式 (Abstract Factory Pattern)
核心概念
Abstract Factory是一种创建型设计模式,用于创建一族相关对象的集合,而不是单个对象。它在工厂方法之上进行了抽象,提供接口定义一系列产品族的创建。
核心原则:
- 隐藏产品族的创建复杂性
- 保证产品族的一致性
- 支持产品族的灵活切换
- 将产品族的创建逻辑集中管理
关键术语:
- 产品族 (Product Family) - 相关产品的集合(如 Windows UI族包含Button、Window、TextBox)
- 抽象工厂 (Abstract Factory) - 定义创建产品族接口
- 具体工厂 (Concrete Factory) - 实现具体产品族创建
何时使用 - 触发模式
始终使用:
- 系统需要支持多个相关对象的集合(产品族)
- 需要在多个产品族之间灵活切换
- 希望保证一个产品族内产品的一致性
- 产品族的创建逻辑复杂/分散在多处
触发短语:
- "如何创建一组相关/相通的对象?"
- "需要支持多个主题/风格/变体,且每个涉及多个对象"
- "如何保证选定的产品都来自同一个族?"
- "产品族涉及多个维度的变化"
典型场景:
| 场景 | 产品族 | 工厂 |
|---|---|---|
| UI 组件系统 | {Windows Button, Window, Dialog} vs {Mac Button, Window, Dialog} | WindowsUIFactory vs MacUIFactory |
| 数据库方言 | {MySQL SQL, Optimizer, Connection} vs {PostgreSQL ...} | MySQLDialectFactory vs PostgresDialectFactory |
| 游戏渲染引擎 | {DirectX Texture, Shader, Buffer} vs {OpenGL ...} | DirectXFactory vs OpenGLFactory |
| 操作系统适配 | {Windows FileSystem, Registry, Process} vs {Linux ...} | WindowsAdapterFactory vs LinuxAdapterFactory |
4个实现方法详解
方法1: 简单抽象工厂 (Simple Abstract Factory)
最直接的实现方式 - 为产品族定义接口,具体工厂实现。
// 产品接口
public interface Button {
void render();
void click();
}
public interface TextField {
void render();
void setText(String text);
}
// 产品实现 - Windows 族
public class WindowsButton implements Button {
@Override
public void render() { System.out.println("🪟 Rendering Windows Button"); }
@Override
public void click() { System.out.println("Windows Button clicked"); }
}
public class WindowsTextField implements TextField {
@Override
public void render() { System.out.println("🪟 Rendering Windows TextField"); }
@Override
public void setText(String text) { System.out.println("Windows TextField: " + text); }
}
// 产品实现 - Mac 族
public class MacButton implements Button {
@Override
public void render() { System.out.println("🍎 Rendering Mac Button"); }
@Override
public void click() { System.out.println("Mac Button clicked"); }
}
public class MacTextField implements TextField {
@Override
public void render() { System.out.println("🍎 Rendering Mac TextField"); }
@Override
public void setText(String text) { System.out.println("Mac TextField: " + text); }
}
// 抽象工厂
public interface UIFactory {
Button createButton();
TextField createTextField();
}
// 具体工厂 - Windows
public class WindowsUIFactory implements UIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextField createTextField() {
return new WindowsTextField();
}
}
// 具体工厂 - Mac
public class MacUIFactory implements UIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public TextField createTextField() {
return new MacTextField();
}
}
// 使用
public class Application {
private UIFactory factory;
public Application(UIFactory factory) {
this.factory = factory;
}
public void renderUI() {
Button button = factory.createButton();
TextField textField = factory.createTextField();
button.render();
textField.render();
}
public static void main(String[] args) {
// 根据配置选择工厂
String os = System.getProperty("os.name");
UIFactory factory = os.contains("Windows")
? new WindowsUIFactory()
: new MacUIFactory();
Application app = new Application(factory);
app.renderUI();
}
}
优点: 结构清晰,易于理解和维护 缺点: 添加新产品族需要创建多个新类
方法2: 配置驱动型工厂 (Configuration-Driven Factory)
使用配置或反射动态决定使用哪个产品族,减少硬编码。
public class ConfigurableUIFactory implements UIFactory {
private String platform;
private Map<String, Class<?>> productRegistry;
public ConfigurableUIFactory(String platform) {
this.platform = platform;
this.productRegistry = new HashMap<>();
loadConfiguration(platform);
}
private void loadConfiguration(String platform) {
// 从配置文件加载
if ("WINDOWS".equalsIgnoreCase(platform)) {
productRegistry.put("Button", WindowsButton.class);
productRegistry.put("TextField", WindowsTextField.class);
} else if ("MAC".equalsIgnoreCase(platform)) {
productRegistry.put("Button", MacButton.class);
productRegistry.put("TextField", MacTextField.class);
}
}
@Override
public Button createButton() {
try {
Class<?> clazz = productRegistry.get("Button");
return (Button) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create Button", e);
}
}
@Override
public TextField createTextField() {
try {
Class<?> clazz = productRegistry.get("TextField");
return (TextField) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create TextField", e);
}
}
}
优点: 通过配置切换产品族,无需重新编译 缺点: 运行时反射开销,异常处理复杂
方法3: 单例结合的工厂 (Singleton-Combined Factory)
确保每个产品族的工厂只有一个实例(资源共享)。
public class SingletonUIFactory {
private static final Map<String, UIFactory> instances = new HashMap<>();
static {
instances.put("WINDOWS", new WindowsUIFactory());
instances.put("MAC", new MacUIFactory());
}
private SingletonUIFactory() {}
public static UIFactory getInstance(String platform) {
UIFactory factory = instances.get(platform);
if (factory == null) {
throw new IllegalArgumentException("Unknown platform: " + platform);
}
return factory;
}
// 获取当前系统的工厂
public static UIFactory getCurrentFactory() {
String os = System.getProperty("os.name");
return getInstance(os.contains("Windows") ? "WINDOWS" : "MAC");
}
}
// 使用
UIFactory factory = SingletonUIFactory.getCurrentFactory();
优点: 资源共享,单一全局访问点 缺点: 全局性可能导致测试困难
方法4: 动态注册工厂 (Dynamic Registry Factory)
允许在运行时注册和注销产品族,支持插件化架构。
public class PluggableUIFactory implements UIFactory {
private static final Map<String, UIFactory> registry = new ConcurrentHashMap<>();
private String platformId;
// 动态注册工厂
public static void register(String platformId, UIFactory factory) {
registry.put(platformId, factory);
System.out.println("Registered factory for: " + platformId);
}
// 动态注销工厂
public static void unregister(String platformId) {
registry.remove(platformId);
System.out.println("Unregistered factory for: " + platformId);
}
public PluggableUIFactory(String platformId) {
if (!registry.containsKey(platformId)) {
throw new IllegalArgumentException("No factory registered for: " + platformId);
}
this.platformId = platformId;
}
@Override
public Button createButton() {
return registry.get(platformId).createButton();
}
@Override
public TextField createTextField() {
return registry.get(platformId).createTextField();
}
}
// 使用
public class Main {
public static void main(String[] args) {
// 在运行时注册工厂
PluggableUIFactory.register("WINDOWS", new WindowsUIFactory());
PluggableUIFactory.register("MAC", new MacUIFactory());
// 使用注册的工厂
UIFactory factory = new PluggableUIFactory("WINDOWS");
Button button = factory.createButton();
button.render();
// 动态添加新的产品族
PluggableUIFactory.register("LINUX", new LinuxUIFactory());
}
}
优点: 高度灵活,支持运行时插件 缺点: 复杂度高,需要仔细管理注册表
常见问题与解决方案
❌ 问题1: 产品族的管理和扩展
症状: 添加新的产品族时,需要修改多个地方;添加新的产品类型时,需要修改所有工厂类。
根本原因: 产品族边界不清晰,扩展点不明确。
解决方案:
// ✅ 改进:使用Builder模式来管理产品族的复杂构建
public class UIFactoryBuilder {
private Map<String, Class<?>> buttonImpls = new HashMap<>();
private Map<String, Class<?>> textFieldImpls = new HashMap<>();
public UIFactoryBuilder registerButton(String platform, Class<?> impl) {
buttonImpls.put(platform, impl);
return this;
}
public UIFactoryBuilder registerTextField(String platform, Class<?> impl) {
textFieldImpls.put(platform, impl);
return this;
}
public UIFactory build(String platform) {
return new ConfigurableUIFactory(platform, buttonImpls, textFieldImpls);
}
}
// 使用
UIFactory factory = new UIFactoryBuilder()
.registerButton("WINDOWS", WindowsButton.class)
.registerTextField("WINDOWS", WindowsTextField.class)
.build("WINDOWS");
❌ 问题2: 产品族一致性保证
症状: 某个工厂返回的产品来自不同的族(混杂),导致风格不一致。
根本原因: 工厂实现不规范,产品隔离不足。
解决方案:
// ✅ 使用版本控制和验证确保一致性
public abstract class BaseUIFactory implements UIFactory {
protected String version;
public BaseUIFactory(String version) {
this.version = version;
}
// 验证产品一致性
protected final void validateConsistency(Button button, TextField textField) {
if (!button.getVersion().equals(textField.getVersion())) {
throw new IllegalStateException(
"Inconsistent product family: " +
button.getVersion() + " vs " + textField.getVersion()
);
}
}
}
public class ConsistentWindowsUIFactory extends BaseUIFactory {
public ConsistentWindowsUIFactory() {
super("1.0");
}
@Override
public Button createButton() {
return new VersionedWindowsButton("1.0");
}
@Override
public TextField createTextField() {
return new VersionedWindowsTextField("1.0");
}
}
❌ 问题3: 产品族扩展成本
症状: 添加新的产品类型时,需要修改所有工厂类;代码改动范围大,容易引入Bug。
根本原因: 产品族的接口设计不够灵活。
解决方案:
// ✅ 使用组合和装饰器扩展产品族
public interface UIProduct {
String getType();
void render();
}
public class ProductFactory {
private UIFactory delegate;
private List<ProductDecorator> decorators;
public ProductFactory(UIFactory delegate) {
this.delegate = delegate;
this.decorators = new ArrayList<>();
}
public ProductFactory addDecorator(ProductDecorator decorator) {
decorators.add(decorator);
return this;
}
public Button createButton() {
Button button = delegate.createButton();
for (ProductDecorator decorator : decorators) {
button = decorator.decorate(button);
}
return button;
}
}
// 新增产品类型无需修改工厂
public interface ProductDecorator {
Button decorate(Button button);
}
public class TooltipDecorator implements ProductDecorator {
@Override
public Button decorate(Button button) {
return new TooltipButton(button);
}
}
❌ 问题4: 产品序列化和反序列化
症状: 需要保存和恢复产品状态,但工厂创建的对象难以序列化。
根本原因: 序列化信息不完整或工厂引用无法恢复。
解决方案:
// ✅ 实现序列化工厂
public class SerializableUIFactory implements UIFactory, Serializable {
private String platformId;
public SerializableUIFactory(String platformId) {
this.platformId = platformId;
}
@Override
public Button createButton() {
Button button = getPlatformFactory().createButton();
return new SerializableButton(button, platformId);
}
@Override
public TextField createTextField() {
TextField field = getPlatformFactory().createTextField();
return new SerializableTextField(field, platformId);
}
private UIFactory getPlatformFactory() {
if ("WINDOWS".equals(platformId)) {
return new WindowsUIFactory();
}
return new MacUIFactory();
}
}
public class SerializableButton implements Button, Serializable {
private Button delegate;
private String platformId;
// 序列化后可通过工厂恢复
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
this.delegate = SerializableUIFactory.getInstance(platformId).createButton();
}
}
与其他模式的关系
| 模式 | 关系 | 使用场景差异 |
|---|---|---|
| 工厂方法 | Abstract Factory 通常使用工厂方法创建产品 | 工厂方法:单个产品;Abstract Factory:产品族 |
| 单例 | 常结合使用,确保工厂唯一性 | 需要全局一致性时组合使用 |
| 生成器 | 可用于复杂产品族的创建 | 产品族构建复杂时结合使用 |
| 适配器 | 用于兼容旧系统的产品族 | 需要与既有系统集成时使用 |
| 装饰器 | 可用于扩展产品族功能 | 需要动态添加产品功能时组合使用 |
最佳实践
✅ 清晰的产品族定义 - 明确界定哪些产品属于一个族
✅ 版本管理 - 为产品族版本化,避免混杂
✅ 配置化 - 优先使用配置而非硬编码决定工厂选择
✅ 一致性保证 - 在工厂中验证产品一致性
✅ 扩展点清晰 - 明确新产品类型的添加流程
✅ 避免过度设计 - 产品族较少时不必使用
何时避免使用
❌ 系统只有单个产品(使用工厂方法)
❌ 产品家族变化频繁且无规律(可能设计不当)
❌ 产品间没有明显关联(可能不算一个族)
❌ 过度预测未来需求(导致过度设计)
真实使用场景
场景1: 数据库方言工厂
public interface SQLDialect {
String getDateFormatFunction();
String getPageClause(long offset, long limit);
String getCountQuery(String sql);
}
public class MySQLDialect implements SQLDialect {
@Override
public String getDateFormatFunction() { return "DATE_FORMAT"; }
@Override
public String getPageClause(long offset, long limit) {
return "LIMIT " + offset + ", " + limit;
}
@Override
public String getCountQuery(String sql) {
return "SELECT COUNT(*) FROM (" + sql + ")";
}
}
public class PostgresDialect implements SQLDialect {
@Override
public String getDateFormatFunction() { return "TO_CHAR"; }
@Override
public String getPageClause(long offset, long limit) {
return "OFFSET " + offset + " LIMIT " + limit;
}
@Override
public String getCountQuery(String sql) {
return "SELECT COUNT(*) FROM (" + sql + ") AS t";
}
}
// 应用:ORM框架根据数据库类型选择方言
public class QueryBuilder {
private SQLDialect dialect;
public QueryBuilder(String dbType) {
this.dialect = DialectFactory.getDialect(dbType);
}
public String buildQuery(String sql, long offset, long limit) {
return sql + " " + dialect.getPageClause(offset, limit);
}
}
场景2: 游戏渲染引擎工厂
跨平台游戏引擎通常需要支持不同的渲染API(DirectX、OpenGL、Metal)。抽象工厂确保图片、着色器、缓冲区成套来自同一个渲染后端。
场景3: 云服务商适配工厂
多云架构中使用抽象工厂为不同云服务商(AWS、Azure、GCP)提供统一接口。每个工厂创建该云商的全套服务(存储、计算、网络)。