抽象工厂模式用于一次性创建一组强关联、主题一致的对象(如WinButton+WinTextBox),当客户端需协同获取多个同族对象且支持主题切换时适用;仅创建单个对象则应选FactoryMethod或DI注册。

抽象工厂不是“多个工厂拼一起”就完事——它只在需要**同时创建一组强关联、且主题必须一致的对象**时才成立。比如 WinButton 和 WinTextBox 必须成对出现,混用 WebButton 会导致 UI 行为错乱;否则就是过度设计。
怎么判断该用 AbstractFactory 而不是 FactoryMethod
关键看客户端是否需要**一次性拿到多个协同工作的对象**,且这些对象必须来自同一主题族:
- 只创建一个对象(如仅需
IRepository<User>)→ 直接注册 DI 或用FactoryMethod更轻量 - 要创建
IButton+ITextBox+IDialog,且支持 Win/Web/Mobile 切换 → 才值得上AbstractFactory - 所有实现固定、无切换需求 → 硬套抽象工厂只会多出
IButtonFactory、ITextBoxFactory等冗余接口
泛型约束写法比传统四接口更安全
传统写法(IFactory + IButton + ITextBox + 两组实现)要频繁 as 强转,运行时才暴露类型错误。改用泛型基类能把约束提前到编译期:
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 补全直接显示返回
WinButton,不用as WinButton - 编译器能捕获“
WinFactory返回了WebButton”这类错误 - 避免
Activator.CreateInstance或return (IButton)Activator.CreateInstance(type)这类黑盒操作
和 Microsoft.Extensions.DependencyInjection 集成时的坑
别在工厂内部手动 new 具体类型,尤其当产品有依赖或需生命周期管理时:
- 错:
new WinButton(new Logger())→Logger实例无法统一控制 - 对:注册时让 DI 容器接管,工厂方法改为
sp.GetRequiredService<IButton>() - 注意:
IButton和ITextBox必须注册为Transient或Scoped;若注册为Singleton,跨主题调用会导致状态污染(比如WinFactory拿到的是WebButton的单例) - 切换逻辑交给 DI 配置,而不是工厂内部写
if (IsWeb()) ... else ...—— 后者会让工厂难以测试和复用
配置驱动的静态工厂适合小型场景
工具类或脚本场景下,不需要完整类层次,用 static class UIFactory + 配置项更直接。容易踩的坑是把配置解析和对象创建混在一起:
- 错:在
CreateButton()里读appsettings.json→ 无法 mock 测试 - 对:分离配置解析,单独函数
GetThemeFromConfig()返回enum Theme { Win, Web };工厂方法只做switch,返回对应实例
最易被忽略的一点:抽象工厂的“主题一致性”不是靠约定,而是靠类型系统或 DI 容器强制保障。一旦允许混用,整个模式就失去意义。


















