抽象工厂模式的核心判断标准是是否封装一系列相关或相互依赖对象的创建逻辑,且客户端不感知具体类型;仅一个类的工厂接口属简单工厂,多产品族(如IButton与ITextBox)才构成抽象工厂。

抽象工厂模式的核心判断标准是什么
不是“用了 interface + new”就是抽象工厂——关键看是否封装了一系列相关或相互依赖的对象的创建逻辑,且客户端完全不感知具体类型。如果只是为一个类写个工厂接口,那叫简单工厂;如果工厂返回的多个产品(比如 IButton 和 )必须来自同一主题(如 <code>WinUIFactory 或 WebUIFactory),才进入抽象工厂范畴。
用泛型约束 + 抽象基类实现最干净的抽象工厂
避免运行时类型检查和强制转换,把约束移到编译期。常见错误是让工厂方法返回 object 或用 Activator.CreateInstance 动态构造,这既丢失类型安全,又绕过多态优势。
推荐结构:
-
abstract class AbstractFactory<tbutton ttextbox></tbutton>声明两个泛型产品类型 - 子类如
WinFactory : AbstractFactory<winbutton wintextbox></winbutton>明确绑定实现 - 工厂方法直接返回
TButton CreateButton(),调用方无需 cast
这样在 IDE 中能自动补全、编译器可校验一致性,比传统四接口(IFactory + IButton + ITextBox + 两组实现)更紧凑,也更容易单元测试。
用依赖注入容器替代手写工厂类的适用场景
当项目已引入 Microsoft.Extensions.DependencyInjection,硬写 WinFactory 和 WebFactory 反而增加维护成本。此时抽象工厂的“切换”逻辑应交给 DI 容器配置。
实操建议:
- 定义
public interface IThemeFactory { IButton CreateButton(); ITextBox CreateTextBox(); } - 注册时根据环境选择实现:
services.AddSingleton<ithemefactory>(sp => IsWeb() ? new WebFactory() : new WinFactory());</ithemefactory> - 避免在工厂内部 new 具体类型——改用
sp.GetRequiredService<ibutton>()</ibutton>,让 DI 管理生命周期
注意:这种方式下,IButton 和 ITextBox 必须注册为 transient 或 scoped,否则跨主题复用会导致状态污染。
静态工厂 + 配置驱动的轻量替代方案
小型工具类或脚本场景下,不需要完整类层次,用 static class UIFactory + 配置项更直接。容易踩的坑是把配置逻辑(如读取 appsettings.json)和对象创建混在一起,导致无法 mock 测试。
正确做法:
- 分离配置解析:单独函数
GetThemeFromConfig()返回enum Theme { Win, Web } - 工厂方法只做 switch:
return theme switch { Theme.Win => new WinButton(), Theme.Web => new WebButton() }; - 所有 new 操作集中在工厂内,不暴露构造细节给调用方
这种写法没有继承树,但满足抽象工厂的本质目标:解耦客户端与具体产品族。只要后续要加 Mac 主题,只需扩 enum 和 case 分支,不改调用代码。
真正难的不是写出五种写法,而是判断哪一组产品该划进同一个“族”——比如 DarkModeButton 和 LightModeButton 不是不同工厂,而是同一工厂下的变体;而 WinButton 和 WebButton 才是不同工厂。这个边界一旦划错,抽象工厂就变成过度设计。


















