引用的大多数使用依赖注入的例子,我们也可以使用工厂模式来解决。看起来当涉及到使用/设计时,依赖注入和工厂之间的区别是模糊或稀薄的。
曾经有人告诉我,你如何使用它才会有所不同!
我曾经使用StructureMap一个DI容器来解决一个问题,后来我重新设计了它来使用一个简单的工厂,并删除了对StructureMap的引用。
谁能告诉我它们之间的区别在哪里使用什么,这里的最佳实践是什么?
引用的大多数使用依赖注入的例子,我们也可以使用工厂模式来解决。看起来当涉及到使用/设计时,依赖注入和工厂之间的区别是模糊或稀薄的。
曾经有人告诉我,你如何使用它才会有所不同!
我曾经使用StructureMap一个DI容器来解决一个问题,后来我重新设计了它来使用一个简单的工厂,并删除了对StructureMap的引用。
谁能告诉我它们之间的区别在哪里使用什么,这里的最佳实践是什么?
当前回答
依赖注入
而不是实例化部件本身,汽车要求它的功能所需的部件。
class Car
{
private Engine engine;
private SteeringWheel wheel;
private Tires tires;
public Car(Engine engine, SteeringWheel wheel, Tires tires)
{
this.engine = engine;
this.wheel = wheel;
this.tires = tires;
}
}
工厂
将各个部分组合在一起以形成一个完整的对象,并对调用者隐藏具体类型。
static class CarFactory
{
public ICar BuildCar()
{
Engine engine = new Engine();
SteeringWheel steeringWheel = new SteeringWheel();
Tires tires = new Tires();
ICar car = new RaceCar(engine, steeringWheel, tires);
return car;
}
}
结果
正如你所看到的,工厂和DI是相辅相成的。
static void Main()
{
ICar car = CarFactory.BuildCar();
// use car
}
你还记得金发姑娘和三只熊吗?依赖注入有点像这样。这里有三种方法来做同样的事情。
void RaceCar() // example #1
{
ICar car = CarFactory.BuildCar();
car.Race();
}
void RaceCar(ICarFactory carFactory) // example #2
{
ICar car = carFactory.BuildCar();
car.Race();
}
void RaceCar(ICar car) // example #3
{
car.Race();
}
例#1——这是最糟糕的,因为它完全隐藏了依赖关系。如果你把这个方法看作一个黑盒子,你就不会知道它需要一辆车。
例2——这样会好一点,因为我们经过了一家汽车厂,现在我们知道我们需要一辆车。但是这次我们传递的太多了,因为这个方法实际上只需要一个car。我们正在路过一个工厂,只是为了建造汽车,当汽车可以在外面建造的方法和通过。
示例#3—这是理想的,因为该方法要求的正是它所需要的。不要太多也不要太少。我不需要为了创建MockCars而编写MockCarFactory,我可以直接传入mock。它是直接的,界面不会说谎。
Misko Hevery的谷歌技术演讲非常棒,这是我得到我的例子的基础。http://www.youtube.com/watch?v=XcT4yYu_TTs
其他回答
这里的大多数答案都解释了两者的概念差异和实现细节。但是我无法解释在应用上的差异,IMO是最重要的,OP问的是什么。所以让我重新讨论这个话题……
曾经有人告诉我,你如何使用它才会有所不同!
完全正确。在90%的情况下,你可以使用Factory或DI来获取对象引用,通常你最终会使用后者。在另外10%的情况下,使用Factory是唯一正确的方法。这些情况包括通过运行时参数的变量获取对象。是这样的:
IWebClient client = factoryWithCache.GetWebClient(url: "stackoverflow.com",
useCookies: false, connectionTimeout: 120);
在这种情况下,从DI获取客户端是不可能的(或者至少需要一些丑陋的解决方案)。因此,作为决策的一般规则:如果可以在没有任何运行时计算参数的情况下获得依赖项,则首选DI,否则使用Factory。
注入框架是工厂模式的实现。
这完全取决于你的要求。如果您需要在应用程序中实现工厂模式,那么您的需求极有可能由众多注入框架实现中的一个来满足。
只有在任何第三方框架都不能满足您的需求时,您才应该推出自己的解决方案。编写的代码越多,需要维护的代码就越多。代码是一种负债而不是资产。
关于应该使用哪个实现的争论没有理解应用程序的体系结构需求那么重要。
[Factory] ->有一个基于请求参数创建类的类。毕竟,“工厂”在现实世界中也为你制造“物品”。你可以让你的汽车供应商工厂生产(免费:)特斯拉。1给你。
[DI] ->一个(服务)容器,用于存储接口(压缩类)。你不关心创建对象。你只需要让某个人/某个地方实现它,细节和其他东西对你、调用者或消费者都不重要。
DI是SOLID原则中“D”的基础。
如果传递的参数可以在工厂中分组,那么它也是构造函数过度注入的一个很好的解决方案,看看下面的代码*):
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();
}
}