引用的大多数使用依赖注入的例子,我们也可以使用工厂模式来解决。看起来当涉及到使用/设计时,依赖注入和工厂之间的区别是模糊或稀薄的。
曾经有人告诉我,你如何使用它才会有所不同!
我曾经使用StructureMap一个DI容器来解决一个问题,后来我重新设计了它来使用一个简单的工厂,并删除了对StructureMap的引用。
谁能告诉我它们之间的区别在哪里使用什么,这里的最佳实践是什么?
引用的大多数使用依赖注入的例子,我们也可以使用工厂模式来解决。看起来当涉及到使用/设计时,依赖注入和工厂之间的区别是模糊或稀薄的。
曾经有人告诉我,你如何使用它才会有所不同!
我曾经使用StructureMap一个DI容器来解决一个问题,后来我重新设计了它来使用一个简单的工厂,并删除了对StructureMap的引用。
谁能告诉我它们之间的区别在哪里使用什么,这里的最佳实践是什么?
当前回答
如果传递的参数可以在工厂中分组,那么它也是构造函数过度注入的一个很好的解决方案,看看下面的代码*):
public AddressModelFactory(IAddressAttributeService addressAttributeService,
IAddressAttributeParser addressAttributeParser,
ILocalizationService localizationService,
IStateProvinceService stateProvinceService,
IAddressAttributeFormatter addressAttributeFormatter)
{
this._addressAttributeService = addressAttributeService;
this._addressAttributeParser = addressAttributeParser;
this._localizationService = localizationService;
this._stateProvinceService = stateProvinceService;
this._addressAttributeFormatter = addressAttributeFormatter;
}
看看构造函数,你只需要在那里传递IAddressModelFactory,所以参数更少*):
public CustomerController(IAddressModelFactory addressModelFactory,
ICustomerModelFactory customerModelFactory,
IAuthenticationService authenticationService,
DateTimeSettings dateTimeSettings,
TaxSettings taxSettings,
ILocalizationService localizationService,
IWorkContext workContext,
IStoreContext storeContext,
ICustomerService customerService,
ICustomerAttributeParser customerAttributeParser,
ICustomerAttributeService customerAttributeService,
IGenericAttributeService genericAttributeService,
ICustomerRegistrationService customerRegistrationService,
ITaxService taxService,
CustomerSettings customerSettings,
AddressSettings addressSettings,...
你可以看到在CustomerController中传递了很多参数,是的,你可以看到这是构造函数的过度注入,但这就是DI的工作方式。CustomerController没有任何问题。
*)代码来自nopCommerce。
其他回答
我的想法:
依赖注入:将协作者作为参数传递给构造函数。 依赖注入框架:一个通用的、可配置的工厂,用于创建对象,并将其作为参数传递给构造函数。
工厂设计模式
工厂设计模式的特点是
一个接口 实现类 一个工厂
当你这样问自己时,你可以观察到一些事情
工厂什么时候为实现类创建对象——运行时还是编译时? 如果您想在运行时切换实现,该怎么办?-不可能
这些是由依赖注入处理的。
依赖注入
您可以使用不同的方式注入依赖项。为了简单起见,让我们使用接口注入
在DI中,容器创建所需的实例,并将它们“注入”到对象中。
这样就消除了静态实例化。
例子:
public class MyClass{
MyInterface find= null;
//Constructor- During the object instantiation
public MyClass(MyInterface myInterface ) {
find = myInterface ;
}
public void myMethod(){
find.doSomething();
}
}
DI为您提供了一个组合根,这是连接对象图的一个集中位置。这往往使对象依赖关系非常显式,因为对象确切地要求它们所需要的东西,并且只有一个地方可以得到它。
组合根是一种清晰而直接的关注点分离。被注入的对象应该不依赖于DI机制,无论是第三方容器还是DIY DI。DI应该是不可见的。
工厂往往更加分散。不同的对象使用不同的工厂,工厂表示对象与其实际依赖关系之间的额外间接层。这个附加层将自己的依赖项添加到对象图中。工厂不是看不见的。工厂是一个中间商。
因此,更新工厂的问题更大:因为工厂是业务逻辑的依赖项,修改它们可能会产生连锁反应。组合根不是业务逻辑的依赖项,因此可以单独修改它。
GoF提到了更新抽象工厂的困难。他们的部分解释被引用在这里的回答中。将DI与工厂进行对比也与ServiceLocator是否是反模式这个问题有很多相似之处。
最终,选择哪个答案可能是固执己见的;但我认为这可以归结为一个工厂是一个中间人。问题在于,除了提供产品之外,这个中间商是否还能通过增加额外价值来发挥自己的作用。因为如果你能在没有中间商的情况下得到同样的产品,那为什么不把中间商去掉呢?
一个图表有助于说明其中的区别。
理论
这里有两点需要考虑:
谁创建对象:
[Factory]:你必须写如何创建对象。您有独立的Factory类,其中包含创建逻辑。 [依赖注入]:在实际情况下,这是由外部框架完成的(例如在Java中是spring/ejb/guice)。注入“神奇地”发生,无需显式地创建新对象。
它管理的对象类型:
[Factory]:通常负责有状态对象的创建 [依赖注入]:更可能创建无状态对象
关于如何在一个项目中同时使用工厂注入和依赖注入的实例
我们想要建造什么
用于创建包含多个名为orderline的条目的订单的应用程序模块。
体系结构
让我们假设我们想要创建以下分层架构:
域对象可以是存储在数据库中的对象。 存储库(DAO)帮助从数据库检索对象。 服务为其他模块提供API。允许对订单模块进行操作。
域层和工厂的使用
数据库中的实体是Order和OrderLine。Order可以有多个orderline。
现在是重要的设计部分。这个模块之外的模块是否应该自己创建和管理orderline ?不。只有当订单与之关联时,订单行才应该存在。最好能将内部实现隐藏到外部类。
但是如何在不了解OrderLines的情况下创建Order呢?
工厂
想要创建新订单的人使用了OrderFactory(它将隐藏关于我们如何创建订单的细节)。
这就是它在IDE中的样子。域包外部的类将使用OrderFactory而不是Order内部的构造函数。
依赖注入 依赖注入更常用于无状态层,如存储库和服务。
OrderRepository和OrderService由依赖注入框架管理。 存储库负责管理数据库上的CRUD操作。Service注入存储库并使用它来保存/查找正确的域类。
I believe, 3 important aspects govern objects and their usage: 1. Instantiation (of a class together with initialisation if any). 2. Injection (of the instance so created) where it's required. 3. Life cycle management (of the instance so created). Using Factory pattern, the first aspect (instantiation) is achieved but the remaining two is questionable. The class that uses other instances must hardcode the factories (instead of instances being created) which hinders loose coupling abilities. Moreover, life cycle management of instances becomes a challenge in a large application where a factory is used in multiple places (particularly, if the factory doesn't manage the life cycle of the instance it returns, it gets ugly). Using a DI (of IoC pattern) on the other hand, all the 3 are abstracted outside the code (to the DI container) and the managed bean needs nothing about this complexity. Loose Coupling, a very important architectural goal can be achieved quiet comfortably. Another important architectural goal, the separation of concerns can be achieved much better than factories.
尽管工厂可能适用于小型应用程序,但大型应用程序最好选择DI而不是工厂。