为什么无参抽象工厂方法属于泄漏的抽象?
核心概念解释
「给定服务的更多实例」的含义
你理解的没错,这个表述就是指多次调用工厂的Create方法会返回多个独立的IProductRepository实例,而非全局复用同一个单例实例。
无参抽象工厂的核心问题
你提到的带参工厂和无参工厂看起来都会生成多实例,但二者的本质差异是:多实例的触发原因是属于业务契约的合理部分,还是属于某个具体实现的内部细节。
先看被标注为坏味道的无参工厂定义:
// code smell public interface IProductRepositoryFactory { IProductRepository Create(); }
它的问题主要有三点:
1. 契约隐含了非通用的实现约束
接口是公开的业务契约,无参Create方法的存在相当于向所有调用方和实现方宣告:IProductRepository的实例需要每次调用都生成新的,调用方需要自行负责实例的生命周期管理(包括释放、缓存等逻辑)。
但这个约束本质上是某个具体实现的特殊要求:比如你用EF Core实现IProductRepository时,因为DbContext线程不安全需要每次请求生成新实例,这只是EF实现的内部细节,换做内存模拟实现、静态缓存实现根本不需要多实例。把单个实现的特殊要求写到通用接口契约里,就是典型的实现细节泄漏。
2. 绑架了所有接口实现
所有实现IProductRepositoryFactory的类都必须遵守无参Create的契约:
- 不需要多实例的实现要么违背后端逻辑强制每次返回新实例,浪费资源;
- 要么偷偷返回单例,但这和契约隐含的「每次调用返回新实例」的语义冲突,如果调用方按照契约逻辑用完就释放实例,会导致其他引用该单例的代码运行异常。
3. 增加调用方的不必要负担
如果没有这个无参工厂,调用方只需要直接注入IProductRepository即可,生命周期完全由DI容器统一管理。但无参工厂的存在迫使调用方必须额外关心实例的创建、缓存、释放逻辑,这些都不是调用方业务逻辑应该关心的内容。
和带参工厂的本质区别
看你提到的带参工厂定义:
public interface IProductRepositoryFactory { IProductRepository Create(string type); }
带参工厂的多实例是业务需求驱动的,属于抽象契约的合理部分:
- 参数
type的含义是接口层面明确定义的,调用方明确知道传入不同的type会得到不同类型的IProductRepository实现(比如传入Sql得到SQL Server实现,传入Memory得到内存模拟实现),这是业务上明确需要的差异化逻辑,和具体实现细节无关。 - 就算多次调用返回新实例,也是调用方主动传入参数触发的,符合调用方的业务预期,不存在隐含的契约约束。
- 不会绑架实现:如果业务上要求某个
type对应的实现是单例,工厂完全可以内部缓存该类型的实例,每次返回同一个,这个逻辑是工厂内部的实现细节,不会暴露给调用方,也不会违反契约。
内容的提问来源于stack exchange,提问作者user16276760
相关产品推荐
相关产品推荐

