抽象工厂模式
大约 11 分钟约 3242 字
抽象工厂模式
简介
抽象工厂(Abstract Factory)提供一组创建相关或依赖对象的接口,而无需指定具体类。理解抽象工厂与工厂方法的区别,有助于在产品族场景中选择合适的创建型模式。
抽象工厂模式是工厂方法模式的泛化版本。工厂方法创建一种产品,抽象工厂创建一族产品。这里的"产品族"指的是一组相互关联或相互依赖的产品,例如跨平台 UI 组件(Windows 按钮 + Windows 文本框)、数据库访问组件(SQL Server 连接 + SQL Server 命令)、日志组件(文件日志 + 控制台日志)等。
GoF 对抽象工厂模式的定义为:"提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。" 关键词是"一系列"—— 抽象工厂的核心是创建一组相关对象,并保证这些对象之间的兼容性。
特点
结构分析
UML 类图
+--------------------+
| IAbstractFactory |
+--------------------+
| +CreateButton() |
| +CreateTextBox() |
| +CreateCheckBox() |
+--------------------+
^
|
+-----------+-----------+
| |
+------------------+ +------------------+
| WindowsFactory | | MacOSFactory |
+------------------+ +------------------+
| +CreateButton() | | +CreateButton() |
| +CreateTextBox() | | +CreateTextBox() |
| +CreateCheckBox()| | +CreateCheckBox()|
+------------------+ +------------------+
+--------------------+
| IButton |
+--------------------+
^
+-----------+-----------+
| |
+------------------+ +------------------+
| WindowsButton | | MacOSButton |
+------------------+ +------------------+产品族矩阵
Windows macOS Linux
按钮(IButton) WinBtn MacBtn LinuxBtn
文本框(ITextBox) WinTxt MacTxt LinuxTxt
复选框(ICheck) WinChk MacChk LinuxChk
^ ^ ^ ^
| | | |
产品族A 产品族B 产品族C 产品族D实现
跨平台 UI 组件工厂
// 抽象产品
public interface IButton
{
void Render();
void OnClick(Action handler);
}
public interface ITextBox
{
void Render();
string Text { get; set; }
void SetPlaceholder(string text);
}
public interface ICheckBox
{
void Render();
bool IsChecked { get; set; }
void OnChanged(Action<bool> handler);
}
// Windows 实现
public class WindowsButton : IButton
{
private Action? _clickHandler;
public void Render() => Console.WriteLine("[Windows] 渲染标准按钮");
public void OnClick(Action handler) => _clickHandler = handler;
}
public class WindowsTextBox : ITextBox
{
public string Text { get; set; } = "";
public void Render() => Console.WriteLine($"[Windows] 文本框: {Text}");
public void SetPlaceholder(string text) => Console.WriteLine($"[Windows] 占位符: {text}");
}
public class WindowsCheckBox : ICheckBox
{
public bool IsChecked { get; set; }
private Action<bool>? _changeHandler;
public void Render() => Console.WriteLine($"[Windows] 复选框: {(IsChecked ? "V" : "O")}");
public void OnChanged(Action<bool> handler) => _changeHandler = handler;
}
// macOS 实现
public class MacOSButton : IButton
{
public void Render() => Console.WriteLine("[macOS] 渲染圆角按钮");
public void OnClick(Action handler) { /* 绑定事件 */ }
}
public class MacOSTextBox : ITextBox
{
public string Text { get; set; } = "";
public void Render() => Console.WriteLine($"[macOS] 圆角文本框: {Text}");
public void SetPlaceholder(string text) => Console.WriteLine($"[macOS] 占位符: {text}");
}
public class MacOSCheckBox : ICheckBox
{
public bool IsChecked { get; set; }
public void Render() => Console.WriteLine($"[macOS] 开关: {(IsChecked ? "ON" : "OFF")}");
public void OnChanged(Action<bool> handler) { }
}
// 抽象工厂
public interface IUIFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
ICheckBox CreateCheckBox();
}
// 具体工厂
public class WindowsUIFactory : IUIFactory
{
public IButton CreateButton() => new WindowsButton();
public ITextBox CreateTextBox() => new WindowsTextBox();
public ICheckBox CreateCheckBox() => new WindowsCheckBox();
}
public class MacOSUIFactory : IUIFactory
{
public IButton CreateButton() => new MacOSButton();
public ITextBox CreateTextBox() => new MacOSTextBox();
public ICheckBox CreateCheckBox() => new MacOSCheckBox();
}
// 客户端
public class UIComponent
{
public IButton SubmitButton { get; }
public ITextBox UsernameBox { get; }
public ITextBox PasswordBox { get; }
public ICheckBox RememberMe { get; }
public UIComponent(IUIFactory factory)
{
SubmitButton = factory.CreateButton();
UsernameBox = factory.CreateTextBox();
PasswordBox = factory.CreateTextBox();
RememberMe = factory.CreateCheckBox();
}
public void Render()
{
UsernameBox.SetPlaceholder("用户名");
PasswordBox.SetPlaceholder("密码");
SubmitButton.Render();
UsernameBox.Render();
PasswordBox.Render();
RememberMe.Render();
}
}数据库访问工厂
// 抽象产品
public interface IDbConnection : IDisposable
{
void Open();
IDbCommand CreateCommand();
}
public interface IDbCommand : IDisposable
{
string CommandText { get; set; }
IDbDataReader ExecuteReader();
int ExecuteNonQuery();
}
public interface IDbDataReader : IDisposable
{
bool Read();
object this[string name] { get; }
}
// 抽象工厂
public interface IDbFactory
{
IDbConnection CreateConnection(string connectionString);
IDbCommand CreateCommand(IDbConnection connection);
IDbDataReader CreateReader(IDbCommand command);
}
// SQL Server 工厂
public class SqlServerFactory : IDbFactory
{
public IDbConnection CreateConnection(string connStr) => new SqlServerConnection(connStr);
public IDbCommand CreateCommand(IDbConnection conn) => new SqlServerCommand(conn);
public IDbDataReader CreateReader(IDbCommand cmd) => new SqlServerDataReader(cmd);
}
// PostgreSQL 工厂
public class PostgresFactory : IDbFactory
{
public IDbConnection CreateConnection(string connStr) => new PostgresConnection(connStr);
public IDbCommand CreateCommand(IDbConnection conn) => new PostgresCommand(conn);
public IDbDataReader CreateReader(IDbCommand cmd) => new PostgresDataReader(cmd);
}
// 使用 — 配合依赖注入
services.AddScoped<IDbFactory>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
var provider = config.GetValue<string>("DatabaseProvider");
return provider switch
{
"SqlServer" => new SqlServerFactory(),
"Postgres" => new PostgresFactory(),
_ => throw new NotSupportedException($"不支持的数据库: {provider}")
};
});实战:消息通知工厂
不同渠道的消息通知(邮件、短信、站内信、WebSocket推送)构成一个产品族。
// 抽象产品 — 消息发送器
public interface IMessageSender
{
Task<SendResult> SendAsync(string recipient, string content);
}
// 抽象产品 — 消息模板渲染器
public interface IMessageTemplateRenderer
{
string Render(string templateName, Dictionary<string, string> variables);
}
// 抽象产品 — 消息状态追踪器
public interface IMessageTracker
{
Task MarkAsSentAsync(string messageId);
Task<MessageStatus> GetStatusAsync(string messageId);
}
public record SendResult(bool Success, string? MessageId = null, string? Error = null);
public enum MessageStatus { Pending, Sent, Delivered, Failed }
// 抽象工厂
public interface IMessageFactory
{
IMessageSender CreateSender();
IMessageTemplateRenderer CreateTemplateRenderer();
IMessageTracker CreateTracker();
}
// 邮件渠道工厂
public class EmailMessageFactory : IMessageFactory
{
private readonly string _smtpHost;
private readonly int _smtpPort;
public EmailMessageFactory(string smtpHost, int smtpPort)
{
_smtpHost = smtpHost;
_smtpPort = smtpPort;
}
public IMessageSender CreateSender() => new EmailSender(_smtpHost, _smtpPort);
public IMessageTemplateRenderer CreateTemplateRenderer() => new EmailTemplateRenderer();
public IMessageTracker CreateTracker() => new EmailTracker();
}
// 短信渠道工厂
public class SmsMessageFactory : IMessageFactory
{
public IMessageSender CreateSender() => new SmsSender();
public IMessageTemplateRenderer CreateTemplateRenderer() => new SmsTemplateRenderer();
public IMessageTracker CreateTracker() => new SmsTracker();
}
// 站内信工厂
public class InAppMessageFactory : IMessageFactory
{
public IMessageSender CreateSender() => new InAppSender();
public IMessageTemplateRenderer CreateTemplateRenderer() => new InAppTemplateRenderer();
public IMessageTracker CreateTracker() => new InAppTracker();
}
// 消息服务 — 依赖抽象工厂
public class NotificationService
{
private readonly IMessageFactory _factory;
public NotificationService(IMessageFactory factory)
{
_factory = factory;
}
public async Task<SendResult> SendNotificationAsync(
string recipient, string templateName,
Dictionary<string, string> variables)
{
var renderer = _factory.CreateTemplateRenderer();
var sender = _factory.CreateSender();
var tracker = _factory.CreateTracker();
// 1. 渲染模板
var content = renderer.Render(templateName, variables);
// 2. 发送消息
var result = await sender.SendAsync(recipient, content);
// 3. 追踪状态
if (result.Success && result.MessageId != null)
await tracker.MarkAsSentAsync(result.MessageId);
return result;
}
}
// DI 注册 — 通过配置选择渠道
// appsettings.json:
// { "Notification": { "Channel": "Email", "SmtpHost": "...", "SmtpPort": 587 } }
builder.Services.AddScoped<IMessageFactory>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
var channel = config["Notification:Channel"];
return channel switch
{
"Email" => new EmailMessageFactory(
config["Notification:SmtpHost"]!,
int.Parse(config["Notification:SmtpPort"]!)),
"Sms" => new SmsMessageFactory(),
"InApp" => new InAppMessageFactory(),
_ => throw new NotSupportedException($"不支持的消息渠道: {channel}")
};
});
builder.Services.AddScoped<NotificationService>();配合泛型与 DI 的现代实现
// 使用泛型抽象工厂,避免为每个产品族创建工厂类
public interface IThemeFactory
{
IThemeColor Colors { get; }
IThemeFont Fonts { get; }
IThemeIcon Icons { get; }
}
public interface IThemeColor { string Primary { get; } string Background { get; } string Text { get; } }
public interface IThemeFont { string Heading { get; } string Body { get; } double BaseSize { get; } }
public interface IThemeIcon { string GetIcon(string name); }
public class DarkThemeFactory : IThemeFactory
{
public IThemeColor Colors => new DarkColors();
public IThemeFont Fonts => new DefaultFonts();
public IThemeIcon Icons => new MaterialIcons();
}
public class LightThemeFactory : IThemeFactory
{
public IThemeColor Colors => new LightColors();
public IThemeFont Fonts => new DefaultFonts();
public IThemeIcon Icons => new MaterialIcons();
}
// 主题管理器
public class ThemeManager
{
private IThemeFactory _factory;
public ThemeManager(IThemeFactory factory) => _factory = factory;
public void ApplyTheme() => Console.WriteLine(
$"主题: {_factory.Colors.Primary}, 字体: {_factory.Fonts.Heading}");
public void SwitchTheme(IThemeFactory newFactory) => _factory = newFactory;
}实战:日志框架抽象工厂
public interface ILogger
{
void Log(LogLevel level, string message);
}
public interface ILogFormatter
{
string Format(LogLevel level, string message);
}
public interface ILogWriter
{
void Write(string formattedMessage);
}
public enum LogLevel { Debug, Info, Warning, Error }
// 抽象工厂
public interface ILoggingFactory
{
ILogger CreateLogger();
ILogFormatter CreateFormatter();
ILogWriter CreateWriter();
}
// 生产环境工厂
public class ProductionLoggingFactory : ILoggingFactory
{
public ILogger CreateLogger() => new ProductionLogger(this);
public ILogFormatter CreateFormatter() => new JsonFormatter();
public ILogWriter CreateWriter() => new FileLogWriter("app.log");
}
// 开发环境工厂
public class DevelopmentLoggingFactory : ILoggingFactory
{
public ILogger CreateLogger() => new DevelopmentLogger(this);
public ILogFormatter CreateFormatter() => new ColorConsoleFormatter();
public ILogWriter CreateWriter() => new ConsoleLogWriter();
}
// DI 注册
services.AddSingleton<ILoggingFactory, ProductionLoggingFactory>();抽象工厂 vs 工厂方法
工厂方法 抽象工厂
+--------+ +--------+
| 一个 | | 一族 |
| 产品 | | 产品 |
+--------+ +--------+
| 一个 | | 多个 |
| 工厂 | | 工厂 |
| 方法 | | 方法 |
+--------+ +--------+
| 子类 | | 工厂 |
| 决定 | | 实例 |
| 产品 | | 决定 |
+--------+ +--------+
| 1:1 | | 1:n |
| 关系 | | 关系 |
+--------+ +--------+
选择标准:
- 只创建一种产品 -> 工厂方法
- 创建一组相关产品 -> 抽象工厂
- 抽象工厂内部通常用工厂方法创建每种产品抽象工厂的扩展问题与解决策略
抽象工厂模式的最大缺点是新增产品类型时需要修改所有工厂。以下策略可以缓解这个问题:
// 问题:新增 IProgressBar 产品类型,需要修改所有工厂
// 解决方案 1:使用默认实现减少修改量
public interface IUIFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
ICheckBox CreateCheckBox();
// 新增方法 — 提供默认实现
IProgressBar CreateProgressBar() => new DefaultProgressBar();
}
// 解决方案 2:使用泛型工厂
public class GenericFactory<TButton, TTextBox, TCheckBox>
where TButton : IButton, new()
where TTextBox : ITextBox, new()
where TCheckBox : ICheckBox, new()
{
public IButton CreateButton() => new TButton();
public ITextBox CreateTextBox() => new TTextBox();
public ICheckBox CreateCheckBox() => new TCheckBox();
}
// 使用
var winFactory = new GenericFactory<WindowsButton, WindowsTextBox, WindowsCheckBox>();
// 解决方案 3:使用注册表模式(最灵活)
public class UIFactoryRegistry
{
private readonly Dictionary<Type, Func<object>> _creators = new();
public UIFactoryRegistry Register<TProduct>(Func<TProduct> creator) where TProduct : class
{
_creators[typeof(TProduct)] = creator;
return this;
}
public TProduct Create<TProduct>() where TProduct : class
{
if (_creators.TryGetValue(typeof(TProduct), out var creator))
return (TProduct)creator();
throw new InvalidOperationException($"未注册的产品类型: {typeof(TProduct).Name}");
}
}
// 注册 Windows 产品族
var registry = new UIFactoryRegistry()
.Register<IButton>(() => new WindowsButton())
.Register<ITextBox>(() => new WindowsTextBox())
.Register<ICheckBox>(() => new WindowsCheckBox())
.Register<IProgressBar>(() => new WindowsProgressBar()); // 新增产品只需注册
var button = registry.Create<IButton>();抽象工厂与依赖注入的配合
// 使用工厂委托注入,避免直接依赖工厂接口
// 在 DI 容器中注册具体工厂
builder.Services.AddSingleton<IUIFactory>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
var platform = config["UI:Platform"];
return platform switch
{
"Windows" => new WindowsUIFactory(),
"macOS" => new MacOSUIFactory(),
_ => throw new NotSupportedException($"不支持的平台: {platform}")
};
});
// 使用 ActivatorUtilities 动态创建工厂
builder.Services.AddScoped(typeof(IMessageFactory), sp =>
{
var channel = sp.GetRequiredService<IConfiguration>()["Notification:Channel"];
return channel switch
{
"Email" => ActivatorUtilities.CreateInstance<EmailMessageFactory>(sp),
"Sms" => ActivatorUtilities.CreateInstance<SmsMessageFactory>(sp),
_ => throw new NotSupportedException()
};
});最佳实践
- 按产品族定义工厂:每个工厂创建一组相关的产品,保证产品间的兼容性。
- 配合依赖注入使用:通过 DI 容器管理工厂的生命周期,根据配置选择具体工厂。
- 工厂接口稳定:抽象工厂接口在设计初期就确定,频繁变更会导致大量修改。
- 考虑注册表模式:当产品族很多时,使用注册表代替工厂类,更灵活。
- 新增产品类型要慎重:新增一种产品需要修改所有工厂接口和实现,影响面大。
优点
缺点
总结
抽象工厂适用于创建一组相关对象的场景(如跨平台 UI、多数据库适配)。核心是定义抽象工厂接口,每个具体工厂创建一整套产品。与工厂方法的区别:工厂方法创建单个产品,抽象工厂创建产品族。配合 DI 容器使用更优雅。新增产品族只需新增工厂类,但新增产品类型需要修改所有工厂。建议在产品族固定、类型多的场景使用。
抽象工厂模式的本质价值在于:当你需要创建一组相关且兼容的对象时,抽象工厂通过定义统一的创建接口,让客户端面向抽象编程,同时保证同一族产品之间的兼容性。当你需要切换整个产品族时,只需替换工厂实例即可。
关键知识点
- 模式不是目标,降低耦合和控制变化才是目标。
- 先找变化点、稳定点和协作边界,再决定是否引入模式。
- 同一个模式在不同规模下的收益和代价差异很大。
项目落地视角
- 优先画出参与对象、依赖方向和调用链,再落到代码。
- 把模式放到一个真实场景里,比如支付、规则引擎、工作流或插件扩展。
- 配合单元测试或契约测试,保证重构后的行为没有漂移。
常见误区
- 为了看起来"高级"而套模式。
- 把简单问题拆成过多抽象层,导致阅读和排障都变难。
- 只会背 UML,不会解释为什么这里需要这个模式。
进阶路线
- 继续关注模式之间的组合用法,而不是孤立记忆。
- 从业务建模、演进策略和团队协作角度看模式的适用性。
- 把模式结论沉淀为项目模板、基类或约束文档。
适用场景
- 当你准备把《抽象工厂模式》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在业务规则频繁变化、分支增多或对象协作复杂时引入。
- 当你希望提高扩展性,但又不想把系统拆得过度抽象时,这类主题很有参考价值。
落地建议
- 先识别变化点,再决定是否引入模式,而不是反过来套模板。
- 优先为模式的边界、依赖和调用路径画出简单结构图。
- 把模式落到一个明确场景,例如支付、规则计算、插件扩展或工作流。
排错清单
- 检查抽象层是否过多,导致调用路径和责任不清晰。
- 确认引入模式后是否真的减少了条件分支和重复代码。
- 警惕"为了模式而模式",尤其是在简单业务里。
复盘问题
- 如果把《抽象工厂模式》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《抽象工厂模式》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《抽象工厂模式》最大的收益和代价分别是什么?
