dependency injectionVS工厂模式

大多数的引用dependency injection的例子,我们也可以使用工厂模式来解决。 看起来,当涉及到使用/devise时,dependency injection和工厂之间的区别变得模糊或薄弱。

一旦有人告诉我,它如何使用它,有所作为!

我曾经使用过StructureMap的一个DI容器来解决一个问题,后来我重新devise了一个简单的工厂,去掉了对StructureMap的引用。

谁能告诉我他们之间有什么区别,在哪里使用什么,这里最好的做法是什么?

当使用工厂时,您的代码实际上仍然负责创build对象。 通过DI,您将该责任外包给与您的代码分开的另一个类或框架。

我会build议保持概念简单明了。 dependency injection更多地是松散耦合软件组件的体系结构模式。 工厂模式只是将创build其他类的对象的责任分离到另一个实体的一种方法。 工厂模式可以被称为实施DI的工具。 dependency injection可以用许多方法实现,如使用构造函数的DI,使用映射XML文件等

dependency injection

汽车不要实例化零件本身,而是要求零件起作用。

 class Car { private Engine; private SteeringWheel; private Tires tires; public Car(Engine engine, SteeringWheel wheel, Tires tires) { this.Engine = engine; this.SteeringWheel = wheel; this.Tires = tires; } } 

放在一起做一个完整的对象,并从调用者隐藏具体types。

 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 } 

你还记得金发姑娘和三只熊吗? 那么,dependency injection就是这样的。 这里有三种方法来做同样的事情。

 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 – 这个好一点,因为现在我们知道我们需要一辆汽车,因为我们经过一个汽车厂。 但是这次我们传递得太多了,因为所有的方法实际上都需要一辆车。 当汽车在方法之外被修造并且通过时,我们通过工厂只是为了修造汽车。

例#3 – 这是理想的,因为该方法确切地询问它需要什么。 没有太多或太less。 我不需要写一个MockCarFactory来创buildMockCars,我可以直接通过模拟。它是直接的,接口不是谎言。

Misko Hevery的Google技术讲座令人惊叹,是我从我的例子中得出的基础。 http://www.youtube.com/watch?v=XcT4yYu_TTs

有一些问题很容易用dependency injection来解决,而一些工厂并不那么容易解决这些问题。

一方面是控制和dependency injection(IOC / DI)的反转,另一方面是服务定位器或一套工厂(工厂)之间的一些区别是:

IOC / DI本身就是一个完整的域对象和服务生态系统。 它以你指定的方式为你设置一切。 你的域对象和服务是由容器构造的,不要自己构造:因此它们对容器或任何工厂没有任何依赖关系。 IOC / DI允许极高的可configuration性,将所有configuration集中在应用程序的顶层(GUI,Web前端)中的一个位置(构build容器)。

工厂会抽象出一些域对象和服务的构build。 但是,域对象和服务仍然负责确定如何构build自己以及如何获得所有依赖的东西。 所有这些“主动”依赖性都会一直过滤应用程序中的所有图层。 没有一个地方去configuration一切。

除了实例化和注入之外,生命周期pipe理是依赖容器所承担的职责之一。 容器有时在实例化之后保持对组件的引用的事实是被称为“容器”而不是工厂的原因。 dependency injection容器通常只保留对pipe理生命周期所需的对象的引用,或者对于未来的注入(如单身人士或飞锤)重用。 当configuration为每次调用容器创build一些组件的新实例时,容器通常只是忘记创build的对象。

从: http : //tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html

DI的一个缺点是它不能用逻辑来初始化对象。 例如,当我需要创build一个具有随机名称和年龄的字符时,DI不是工厂模式的select。 有了工厂,我们可以很容易地将随机algorithm从对象创build封装起来,支持一种叫做“封装变化”的devise模式。

我相信DI是工厂的一种抽象层,但他们也提供了超越抽象的好处。 一个真正的工厂知道如何实例化一个types并configuration它。 良好的DI层提供了通过configuration来实例化和configuration多种types的能力。

很显然,对于一些简单types的项目,build设中需要相对稳定的业务逻辑,工厂模式易于理解,实施,运行良好。

OTOH,如果你有一个项目包含许多types的实现你希望经常改变,DI给你灵活的configuration在运行时做到这一点,而不必重新编译你的工厂。

dependency injection(DI)和工厂模式相似的原因是因为它们是软件体系结构的控制反转(IoC)的两个实现。 简而言之,他们是同一个问题的两个解决scheme。

所以要回答这个问题,工厂模式和DI之间的主要区别是如何获得对象引用。 使用dependency injection作为名称暗示引用被注入或提供给您的代码。 使用工厂模式,您的代码必须请求引用,以便您的代码获取对象。 这两个实现都将删除或分离代码与代码所使用的对象引用的基础类或types之间的链接。

值得注意的是,可以编写工厂模式(或者确实是返回返回对象引用的新工厂的工厂的抽象工厂模式)来dynamicselect或链接到运行时请求的对象的types或类。 这使得它们与作为IoC的另一个实现的Service Locator模式非常相似(甚至比DI更相似)。

工厂的devise模式是相当古老(在软件方面),并已经有一段时间了。 由于最近stream行的架构模式IoC,它正在复苏。

我想当谈到IoC的devise模式:注射器注射,定位器定位和工厂已经重构。

我知道这个问题很老,但我想补充我的五分钱,

我认为,dependency injection(DI)在很多方面都像一个可configuration的工厂模式(FP),从这个意义上说,任何你可以用DI做的事情都可以用这样的工厂来完成。

其实,如果你使用spring,例如你可以select自动assembly资源(DI)或者做这样的事情:

 MyBean mb = ctx.getBean("myBean"); 

然后用这个“mb”实例来做任何事情。 这不是一个工厂,将返回一个实例的调用?

我注意到大多数FP例子中唯一真正的区别是,你可以configuration“myBean”是在xml还是在另一个类中,而一个框架将会像工厂一样工作,但除此之外是相同的东西,而你可以有一个肯定有一个工厂,读取configuration文件或获取实施,因为它需要。

如果你问我的意见(我知道你没有),我相信DI做同样的事情,但只是增加了发展的复杂性,为什么?

好吧,首先,为了让你知道什么是你使用DI自动assembly的bean的实现,你必须去configuration本身。

但是…那个承诺怎么样,你不会知道你正在使用的对象的实现? pfft! 当真? 当你使用这样的方法…是不是你写的实现相同? 即使你不这样做,几乎所有的时间你都不知道这个实现是怎么做的?

最后一件事, DI框架承诺给你多less无关紧要 ,如果你正在使用一个框架构build所有的东西, 如果你不得不改变方法或框架,这将不是一件容易的事情…永远! …但是,既然你围绕这个特定的框架做了一切,而不是担心什么是你的业务最好的解决scheme,那么当你这样做时,你将面临一个生物学上的问题。

事实上,我所看到的FP或DI方法的唯一真正的商业应用是,如果你需要改变在运行时使用的实现,但至less我知道的框架不允许你这样做,你必须离开如果你需要使用另一种方法,那么在开发时configuration的一切都是完美的。

所以,如果我有一个在同一个应用程序中的两个范围内执行不同的类(可以说,两个公司持有),我必须configuration框架来创build两个不同的bean,并调整我的代码使用每个。 是不是就像我会写这样的东西:

 MyBean mb = MyBeanForEntreprise1(); //In the classes of the first enterprise MyBean mb = MyBeanForEntreprise2(); //In the classes of the second enterprise 

就像这样:

 @Autowired MyBean mbForEnterprise1; //In the classes of the first enterprise @Autowired MyBean mbForEnterprise2; //In the classes of the second enterprise 

和这个:

 MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise1"); //In the classes of the first enterprise MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise2"); //In the classes of the second enterprise 

在任何情况下,你将不得不改变你的应用程序中的东西,不pipe是类还是configuration文件,但是你必须重新部署它。

做这样的事情不是很好吗:

 MyBean mb = (MyBean)MyFactory.get("mb"); 

那样,你根据login的用户企业设置工厂的代码以在运行时获得正确的实现? 现在这将是有益的。 你可以添加一个新的jar包,新的类,甚至可以在运行时设置规则(如果你打开这个选项,也可以添加一个新的configuration文件),而不会改变现有的类。 这将是一个dynamic的工厂!

那么比起为每个企业编写两个configuration会更有帮助,甚至可能每个企业都有两个不同的应用程序?

你可以告诉我,我不需要在运行时进行切换,所以我configuration了应用程序,如果我inheritance这个类或者使用另一个实现,我只需要更改configuration并重新部署即可。 好的,这也可以用工厂来完成。 说实话,你做了几次? 也许只有当你有一个应用程序将被用在你的公司的其他地方,你会把代码传递给另一个团队,他们会做这样的事情。 但是,嘿,这也可以用工厂来完成,而且用dynamic的工厂会更好!

无论如何,评论部分如果开放给你杀我。

通过dependency injection,客户端不需要事先获得它自己的依赖关系。

有了工厂,有人不得不打电话给那些把生成的对象放到需要的地方。

主要区别在于调用工厂和获取构造对象的这一行完成。

但是对于工厂,你必须在任何地方写这一行,你需要这样一个对象。 使用DI,您只需创build一次布线(使用与创build对象之间的关系),然后在任何地方依靠对象的存在。 另一方面,DI往往需要多一点(多less取决于框架)在准备方面的工作。

我一读到DI就有同样的问题,最后在这个post里。 所以最后这是我的理解,但如果我错了,请纠正我。

“很久以前,小国都有自己的理事机构,依据自己的书面规则来控制和决策,后来形成了一个大政府,取消了所有这些有一套规则(宪法),通过法院执行的小理事机构”

小国王的pipe理机构是“工厂”

大政府是“依赖注射器”。

国际奥委会是一个通过两种方式实施的概念。 依赖创build和dependency injection,Factory / Abstract工厂是依赖创build的例子。 dependency injection是构造函数,设置者和接口。 IOC的核心是不依赖于具体的类,而是定义方法的抽象(比如一个接口/抽象类),并使用该抽象来调用具体类的方法。 像工厂模式一样返回基类或接口。 Similarlyydependency injection使用基类/接口来设置对象的值。

你可以看看这个链接来比较两个(和其他)的方法在一个真实的例子。

基本上,当需求改变时,如果你使用工厂而不是DI,你最终会修改更多的代码。

这也适用于手动DI(即当没有外部框架提供对象的依赖关系,而是在每个构造函数中传递它们时)。

我相信DI是一种configuration或实例化bean的方法。 DI可以用许多方式完成,比如构造函数,setter-getter等

工厂模式只是实例化bean的另一种方式。 这个模式将主要用在使用工厂devise模式创build对象的时候,因为在使用这个模式的时候,你不要configuration一个bean的属性,只实例化对象。

检查这个链接: dependency injection

Binoj,

我不认为你必须select一个。

将依赖类或接口移到类构造函数或setter中的操作遵循DI模式。 您传递给构造函数或集合的对象可以使用Factory实现。

何时使用? 使用开发人员驾驶室中的图案。 他们觉得什么最舒服,最容易理解。

我的想法:

依赖注射:将合作者作为parameter passing给构造者。 dependency injection框架:一个通用的可configuration工厂,用于创build对象作为parameter passing给构造函数。

注入框架是工厂模式的一个实现。

这一切都取决于你的要求。 如果您需要在应用程序中实现工厂模式,很可能您的需求将由无数的注入框架实现之一来满足。

如果任何第三方框架无法满足您的需求,您应该只推出自己的解决scheme。 您编写的代码越多,您需要维护的代码就越多。 代码是一项责任而不是资产。

你应该使用哪个实现的争论并不像理解你的应用程序的架构需求那么重要。

工厂devise模式

工厂devise模式的特点是

  • 一个接口
  • 实现类
  • 一个工厂

当你质疑自己时,你可以观察到几件事

  • 工厂何时为实现类创build对象 – 运行时间还是编译时间?
  • 如果你想在运行时切换实现呢? – 不可能

这些由dependency injection处理。

dependency injection

你可以有不同的方式来注入依赖。 为了简单,可以使用接口注入

在DI中,容器创build所需的实例,并将其“注入”到对象中。

从而消除了静态实例化。

例:

 public class MyClass{ MyInterface find= null; //Constructor- During the object instantiation public MyClass(MyInterface myInterface ) { find = myInterface ; } public void myMethod(){ find.doSomething(); } } 

从面值看他们看起来一样

用非常简单的术语“工厂模式”,“创build模式”有助于为我们创build一个对象 – “定义创build对象的接口”。 如果我们有一个键值对象池(例如Dictionary),将键传递给Factory(我指的是简单工厂模式),则可以parsingType。 任务完成! 另一方面,dependency injection框架(如Structure Map,Ninject,Unity …等)似乎也在做同样的事情。

但是…“不要重新发明轮子”

从build筑的angular度来看,它是一个约束层,“不要重新发明轮子”。

对于企业级应用,DI的概念更多地是定义依赖性的体系结构层。 为了进一步简化这一点,您可以将其视为一个单独的类库项目,该项目执行依赖关系parsing。 主要的应用程序依赖于这个项目,其中依赖parsing器引用其他具体实现和依赖parsing。

除了工厂的“GetType / Create”之外,大多数情况下我们需要更多的function(能够使用XML来定义依赖关系,模拟和unit testing等)。 既然您参考了结构图,请查看结构图function列表 。 这显然不仅仅是解决简单的对象映射。 不要重新发明轮子!

如果你只有一把锤子,一切看起来都像钉子

根据您的要求和您构build的应用程序types,您需要做出select。 如果只有几个项目(可能是一个或两个),并涉及很less的依赖关系,你可以select一个更简单的方法。 这就像使用ADO .Net数据访问,通过使用entity framework进行简单的1或2个数据库调用,在这种情况下引入EF是矫枉过正的。

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

理论

有两点需要考虑:

  1. 谁创build对象

    • [工厂]:你必须写如何创build对象。 你有单独的Factory类,其中包含创build逻辑。
    • [dependency injection]:在实际情况下,通过外部框架来完成(例如Java中的spring / ejb / guice)。 注射“神奇地”发生没有明确创造新的对象
  2. 它pipe理的是什么types的对象:

    • [工厂]:通常负责创build有状态的对象
    • [dependency injection]更可能创build无状态对象

如何在单个项目中使用工厂和dependency injection的实例

  1. 我们想要build立的

用于创build包含多个称为orderline的条目的订单的应用程序模块。

  1. build筑

我们假设我们要创build以下图层体系结构:

在这里输入图像描述

域对象可能是存储在数据库中的对象。 存储库(DAO)帮助从数据库中检索对象。 服务为其他模块提供API。 降低order模块的操作

  1. 域层和工厂的使用

将在数据库中的实体是Order和OrderLine。 订单可以有多个订单行。 订单与订单行之间的关系

现在是重要的devise部分。 这个模块以外的模块应该自己创build和pipe理OrderLines吗? 只有订单关联的订单行才会存在。 如果你能把内部的实施隐藏到课外,那将是最好的。

但是如何在没有OrderLines知识的情况下创buildOrder?

有人想创build使用OrderFactory的新订单(这将隐藏有关我们如何创build订单的细节)。

在这里输入图像描述

多数民众赞成在IDE内部。 domain包之外的类将使用OrderFactory而不是Order的构造函数

  1. dependency injectiondependency injection更常用于无状态层(如存储库和服务)。

OrderRepository和OrderService由dependency injection框架pipe理。 存储库负责pipe理数据库上的CRUD操作。 服务注入存储库并使用它来保存/查找正确的域类。

在这里输入图像描述

我相信,3个重要的方面pipe理对象及其用法:
1. 实例化 (如果有的话,一个类的初始化)。
2. 注入 (所创build的实例)需要的地方。
3. 生命周期pipe理 (所创build的实例)。

使用工厂模式,第一个方面(实例化)已经实现,但其余两个是可疑的。 使用其他实例的类必须对工厂进行硬编码 (而不是正在创build的实例),从而阻碍了松耦合的能力。 而且,在多个地方使用工厂的大型应用程序(尤其是工厂不pipe理它返回的实例的生命周期,变得难看)时,实例的生命周期pipe理成为一个挑战。

另一方面,使用DI(IoC模式),所有的3都被抽象到代码之外(到DI容器),而被pipe理的bean不需要这个复杂性。 松耦合 ,一个非常重要的build筑目标可以安静舒适地实现。 另一个重要的build筑目标,关系的分离可以比工厂更好地实现。

而工厂可能适合小型应用,大型工厂可能更适合selectDI。

在我看来,使用dependency injection要好得多:1.将代码部署在小分区中,因为它可以很好地解耦一个大代码。 2.可testing性是DI可以使用的一种情况,因为您可以轻松地模拟非分离的对象。 通过使用接口,您可以轻松地模拟和testing每个对象。 3.您可以同时修改程序的每个部分,而不需要编写其他部分,因为它的松散解耦。