抽象工厂适用于需同时创建一组强关联且主题可切换的产品对象(如WinButton+WinTextBox),否则易过度设计;应避免冗余接口、运行时反射及DI生命周期错误,优先用泛型约束和配置驱动切换。

抽象工厂不是“多个工厂接口拼一起”就完事——它必须解决“一整套产品族切换”这个具体问题,否则就是过度设计。
怎么判断该用 Abstract Factory 而不是 Factory Method
关键看客户端是否需要同时拿到多个强关联的产品实例,且这些实例必须来自同一主题(比如 WinButton + WinTextBox 不能混用 WebButton)。
- 只创建一个对象(如仅需
IRepository)→ 用FactoryMethod或直接 DI 注册更轻量 - 要创建一组协同工作的对象(
IButton+ITextBox+IDialog),且主题可替换(Win / Web / Mobile)→ 才值得上AbstractFactory - 如果所有实现都固定、无切换需求,硬套抽象工厂只会增加
IButtonFactory、ITextBoxFactory等冗余接口
用泛型约束替代传统四接口写法
传统写法(IFactory + IButton + ITextBox + 两组实现)导致大量重复接口和类型转换。改用泛型基类能压缩层级、提升类型安全:
public abstract class ThemeFactory<TButton, TTextBox>
where TButton : IButton
where TTextBox : ITextBox
{
public abstract TButton CreateButton();
public abstract TTextBox CreateTextBox();
}
public class WinFactory : ThemeFactory<WinButton, WinTextBox>
{
public override WinButton CreateButton() => new();
public override WinTextBox CreateTextBox() => new();
}
- IDE 可直接补全
CreateButton()返回的就是WinButton,不用as WinButton强转 - 编译期就能捕获“返回了
WebButton却声明为WinButton”这类错误 - 避免
Activator.CreateInstance或return (IButton)Activator.CreateInstance(type)这类运行时黑盒操作
和 Microsoft.Extensions.DependencyInjection 集成时的坑
别在工厂内部手动 new 具体类型,尤其当产品有依赖或需生命周期管理时:
- 错:在
WinFactory.CreateButton()里写new WinButton(new Logger())→ 无法统一控制Logger实例 - 对:注册时让 DI 容器接管,工厂方法改为
sp.GetRequiredService<IButton>() - 但注意:
IButton和ITextBox必须注册为Transient(每次新建)或Scoped;若注册为Singleton,跨主题调用会导致状态污染(比如WinFactory拿到的是WebButton的单例) - 切换逻辑交给 DI 配置,而不是工厂内部
if (IsWeb()) ... else ...—— 后者会让工厂难以测试和复用
小型项目里更务实的做法
没多主题需求、只是想解耦创建逻辑?静态工厂 + 枚举配置比抽象工厂干净得多:
public static class UIFactory
{
public static IButton CreateButton(Theme theme) => theme switch
{
Theme.Win => new WinButton(),
Theme.Web => new WebButton(),
_ => throw new ArgumentOutOfRangeException(nameof(theme))
};
}
- 把
Theme从配置读取(如Configuration.GetValue<Theme>("UI:Theme"))单独封装,不要塞进工厂方法里 - 这样单元测试时可直接传入枚举值,无需 mock 配置系统
- 一旦发现主题变多、产品数量上升(比如加了
IDialog、ISlider),再重构为完整抽象工厂也不晚
最容易被忽略的一点:抽象工厂的价值不在“写得有多像 GoF 图”,而在让主题切换成为可配置、可测试、不散落在各处 new 表达式里的单一决策点。没这个前提,代码越“规范”越难维护。


















