引用的大多数使用依赖注入的例子,我们也可以使用工厂模式来解决。看起来当涉及到使用/设计时,依赖注入和工厂之间的区别是模糊或稀薄的。

曾经有人告诉我,你如何使用它才会有所不同!

我曾经使用StructureMap一个DI容器来解决一个问题,后来我重新设计了它来使用一个简单的工厂,并删除了对StructureMap的引用。

谁能告诉我它们之间的区别在哪里使用什么,这里的最佳实践是什么?


当前回答

DI为您提供了一个组合根,这是连接对象图的一个集中位置。这往往使对象依赖关系非常显式,因为对象确切地要求它们所需要的东西,并且只有一个地方可以得到它。

组合根是一种清晰而直接的关注点分离。被注入的对象应该不依赖于DI机制,无论是第三方容器还是DIY DI。DI应该是不可见的。

工厂往往更加分散。不同的对象使用不同的工厂,工厂表示对象与其实际依赖关系之间的额外间接层。这个附加层将自己的依赖项添加到对象图中。工厂不是看不见的。工厂是一个中间商。

因此,更新工厂的问题更大:因为工厂是业务逻辑的依赖项,修改它们可能会产生连锁反应。组合根不是业务逻辑的依赖项,因此可以单独修改它。

GoF提到了更新抽象工厂的困难。他们的部分解释被引用在这里的回答中。将DI与工厂进行对比也与ServiceLocator是否是反模式这个问题有很多相似之处。

最终,选择哪个答案可能是固执己见的;但我认为这可以归结为一个工厂是一个中间人。问题在于,除了提供产品之外,这个中间商是否还能通过增加额外价值来发挥自己的作用。因为如果你能在没有中间商的情况下得到同样的产品,那为什么不把中间商去掉呢?

一个图表有助于说明其中的区别。

其他回答

从表面上看,他们是一样的

简单来说,工厂模式,创建模式帮助我们创建一个对象——“定义一个创建对象的接口”。如果我们有一个键值类型的对象池(例如Dictionary),将键传递给工厂(我指的是简单工厂模式),您可以解析类型。完成工作! 另一方面,依赖注入框架(如结构图、Ninject、Unity等)似乎也在做同样的事情。

但是…“不要白费力气”

从架构的角度来看,这是一个绑定层,“不要白费力气”。

对于企业级应用程序,依赖注入的概念更像是一个定义依赖关系的体系结构层。为了进一步简化,您可以将其视为一个单独的类库项目,它进行依赖解析。主应用程序依赖于这个项目,其中依赖项解析器引用其他具体实现和依赖项解析。

除了来自Factory的“GetType/Create”之外,我们通常还需要更多的特性(使用XML定义依赖关系、模拟和单元测试等)。既然您引用了结构图,那么请查看结构图特性列表。这显然不仅仅是解决简单的对象映射。别白费力气了!

如果你只有一把锤子,那么所有东西看起来都像钉子

根据您的需求和您构建的应用程序类型,您需要做出选择。如果它只有很少的项目(可能是一个或两个..)并且涉及很少的依赖项,您可以选择一个更简单的方法。这就像使用ADO . net数据访问而不是使用实体框架进行简单的1或2个数据库调用,在这种情况下引入EF是多余的。

但是对于一个更大的项目,或者如果你的项目变得更大,我强烈建议有一个带有框架的DI层,并留出空间来改变你使用的DI框架(在主应用程序中使用Facade (Web应用程序,Web Api, Desktop..等)。

当使用工厂时,您的代码实际上仍然负责创建对象。通过DI,你可以将职责外包给另一个类或框架,这与你的代码是分开的。

我的想法:

依赖注入:将协作者作为参数传递给构造函数。 依赖注入框架:一个通用的、可配置的工厂,用于创建对象,并将其作为参数传递给构造函数。

简单来说,依赖注入和工厂方法分别意味着推和拉机制。

拉机制:类间接依赖于工厂方法,工厂方法又依赖于具体类。

推送机制:根组件可以在一个位置配置所有依赖组件,从而促进高维护和松耦合。

使用Factory方法时,创建新对象的责任仍然由类承担(尽管是间接的),而使用依赖注入时,责任是外包的(尽管是以泄露抽象为代价)。

我相信DI是工厂的一种抽象层,但是它们还提供了抽象之外的好处。真正的工厂知道如何实例化单一类型并配置它。好的DI层通过配置提供实例化和配置多种类型的能力。

显然,对于具有一些简单类型的项目(在其构造中需要相对稳定的业务逻辑),工厂模式易于理解、实现并且工作良好。

OTOH,如果您有一个包含许多类型的项目,您希望经常更改这些类型的实现,DI通过其配置为您提供了在运行时执行此操作的灵活性,而无需重新编译工厂。