简单工厂适合轻量场景但非gof模式;工厂方法解决开闭原则,支持产品线横向扩展;抽象工厂仅适用于需成套切换相关对象的产品族场景,如ui主题。

Factory Pattern 在 C# 中不是“要不要用”的问题,而是“用哪种、怎么用才不翻车”的问题。直接结论:简单工厂适合快速验证或配置驱动的轻量场景;工厂方法适合产品线稳定但需横向扩展(如新增 ProductC);抽象工厂只在你明确需要成套切换相关对象(如 IButton + ITextBox 同属 Win 或 Web 主题)时才值得上。
简单工厂别当设计模式用,但可以当实用工具写
它本质就是个带逻辑的 new 封装,不是 GoF 模式,也扛不住中大型项目迭代。常见错误是把它当解耦银弹——结果 CreateProduct(string type) 里堆满 switch 分支,一加新类型就得改工厂、测全量、提 PR。
- 用
enum代替string参数,编译期就能防错,比如ProcessorType.Image比"image"更安全 - 返回类型必须是公共接口或基类,不能是
object,否则下游还得as或is判型 - 别在工厂里读配置文件或连数据库——这些该在启动时完成,工厂只做瞬时构造
- 如果项目已用
IServiceProvider,就别自己new ConcreteProductA(),改用sp.GetRequiredService<iproduct>()</iproduct>,否则生命周期失控
工厂方法模式真正解决开闭原则问题
当你发现产品种类开始变多(比如从 A/B 扩展到 A/B/C/D),且每个产品创建逻辑差异大(比如要传不同参数、初始化不同依赖),SimpleFactory 就该退休了。
- 抽象基类
abstract class ProductFactory声明abstract IProduct CreateProduct(); - 每个具体工厂(
ImageFactory、VideoFactory)只管自己那一摊,新增类型不碰老代码 - 客户端通过依赖注入获取具体工厂实例,而不是
new它——否则又绕回紧耦合 - 注意:工厂类本身也要考虑生命周期。若注册为
Singleton,内部缓存字典必须线程安全(比如用ConcurrentDictionary)
抽象工厂只在“产品族”场景下成立
判断是不是真需要抽象工厂,就看一句话:**你是否必须保证 IButton 和 ITextBox 来自同一实现家族?** 如果只是单独换一个按钮,那是简单工厂;如果换 UI 主题就要整套换,才是抽象工厂。
- 必须先定义抽象产品族接口:
IWidgetFactory含CreateButton()和CreateTextBox() - 每个具体工厂(
WinFactory、WebFactory)返回的对象必须语义一致,不能WinFactory.CreateButton()+WebFactory.CreateTextBox()混搭 - 推荐用泛型抽象工厂避免运行时转型:
abstract class ThemeFactory<tbutton ttextbox></tbutton>,子类绑定具体类型 - 和 DI 配合时,注册逻辑要统一决策点,比如:
services.AddSingleton<iwidgetfactory>(sp => IsWeb() ? new WebFactory() : new WinFactory());</iwidgetfactory>
最容易被忽略的坑:工厂本身的状态与线程安全
没人告诉你工厂类不是纯函数——它可能缓存实例、持有配置快照、甚至维护连接池。一旦注册为 Singleton,多个线程并发调用 CreateXXX() 时,内部非线程安全集合(比如普通 Dictionary)会静默出错,报的却是下游 NullReferenceException,排查极耗时间。
更隐蔽的是:工厂若在构造函数里读取 IConfiguration,而配置源本身是异步加载的(如 Azure App Config),就可能拿到空值——这类问题不会在单元测试里暴露,只在线上偶发。











