依赖注入与工厂模式


496

引用依赖注入的大多数示例,我们也可以使用工厂模式来解决。看起来在使用/设计时,依赖项注入和工厂之间的差异变得模糊或稀薄。

曾经有人告诉我,您如何使用它与众不同!

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

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


21
这两种方法不能互相补充:使用依赖注入来注入工厂类吗?
理查德Ev

20
如果这个问题的答案中包含一些代码,那就太好了!我仍然看不到将DI与使用工厂进行创作有何不同?您只需要替换工厂类中的那一行即可更改创建哪个obj /实现?
gideon

2
@gideon不会强迫您编译应用程序,或者至少编译包含工厂类的模块吗?
lysergic-acid

1
@liortal是的,没错。自发表评论以来,我对DI进行了长期的研究,现在我了解到DI将工厂方法向前迈了一步。
gideon

1
看看这个好答案:stackoverflow.com/questions/4985455/…-他说得很好,并提供了代码示例。
路易斯·佩雷斯

Answers:


292

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


172
DI模式不需要任何框架。您可以通过手动编写执行DI的工厂来进行DI。DI框架使其变得更容易。
Esko Luontola 09年

28
@Perpetualcoder-谢谢@Esko-不要陷入单词框架的含义,这意味着一些先进的第三方图书馆。
willcodejavaforfood

4
+1 @willcode谢谢!因此,您必须改为更改config / xml文件。我知道了。Wiki链接en.wikipedia.org/wiki/…将工厂模式定义为Manually-Injected Dependency
gideon 2010年

5
与更改1行代码相比,我没有获得更改XML行的优势。你能详细说明吗?
拉斐尔·埃昂

1
重复OP回答,将“ DI”替换为“ factory”,仍然有意义。
Edson Medina

219

我建议保持概念简单明了。依赖注入更多是一种松散耦合软件组件的体系结构模式。工厂模式只是将创建其他类的对象的职责分离到另一个实体的一种方法。可以将工厂模式称为实现DI的工具。依赖注入可以通过多种方式实现,例如使用构造函数进行DI,使用映射xml文件等。


4
的确,工厂模式是实现依赖注入的一种方式。对工厂模式使用依赖注入的另一个好处是,DI框架将为您提供灵活的方式来注册针对具体类型的抽象。例如Config,XML或Auto Configuration之类的代码。这种灵活性将使您能够管理对象的生命周期或决定如何以及何时注册接口。
Teoman shipahi 2014年

1
工厂模式比DI提供更高级别的抽象。工厂可以像DI一样提供配置选项,但是它也可以选择隐藏所有这些配置详细信息,以便工厂可以决定在客户不知情的情况下切换到完全不同的实现。DI本身不允许这样做。DI要求客户指定他想要的更改。
神经元

1
好答案。许多人对这个问题感到困惑:“我应该选择哪种模式?” 实际上,设计模式只是在适当情况下解决问题的一种方法。如果您不知道应该使用哪种设计模式,或者“我应该在哪里实现该模式?” 那么您目前不需要。
Benyi '18

2
我认为说“工厂模式”是实现IoC(不是DI)的工具更为准确。请注意,DI是IoC的一种形式
Hooman Bahreini

185

依赖注入

汽车没有实例化零件本身,而是要求其需要起作用的零件。

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();
}

Example#1-这是最糟糕的,因为它完全隐藏了依赖关系。如果您将该方法看成一个黑匣子,您将不会知道它需要一辆汽车。

例子2-更好一点,因为现在我们知道我们需要汽车,因为我们经过汽车制造厂。但是这次我们通过的太多了,因为实际上所有方法都需要一辆汽车。当汽车可以在方法之外制造并通过时,我们正在通过一家工厂来制造汽车。

Example#3-这是理想的,因为该方法确切地要求它需要什么。不会太多或太少。我不必编写MockCarFactory即可创建MockCars,我可以直接传递模拟。它是直接的,接口也不会说谎。

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


1
将工厂模式作为DI注入。
Mangesh Pimpalkar 2014年

7
@PhilGoetz您所描述的听起来更像是Service Locator模式。它们的目标相似,因为它们旨在使服务与消费者脱钩。但是,服务定位器模式有很多缺点。主要是,隐藏依赖项不是一件好事。使用者仍然需要获取其依赖关系,但是与其定义一个清晰的接口,不如“秘密地”传递它们并依赖于全局状态。在该示例中,消费者对工厂一无所知,它只询问需要的东西,而不必关心如何构造需求。
Despertar

1
@MatthewWhited大多数设计决策都需要权衡。使用DI,您可以获得灵活性,透明性和更可测试的系统,但是却要付出胆量。许多人发现这是可以接受的折衷方案。这并不是说DI始终是最好的解决方案,而这个问题并非如此。该示例旨在通过显示每种用法的好坏来说明DI和工厂模式之间的区别。
Despertar

3
这不是DI模式,这只是IoC的实现。DI使用ServiceLocator来确定正确的依赖关系,并在对象创建期间将其注入到构造函数中,您的代码仅将对象的创建与类范围分开。
迭戈·门德斯

3
@DiegoMendes您正在描述的是一个自动执行DI的框架。您所称的ServiceLocator会为您执行DI。我展示的是模式本身,不需要任何花哨的框架。请参阅stackoverflow.com/a/140655/1160036,或请参阅 en.wikipedia.org/wiki/…:“ [框架列表]支持依赖项注入,但不需要进行依赖项注入”。另外,“注入是将依赖项传递给依赖对象”。您已经使一个非常漂亮的简单概念复杂化了。
Despertar

50

依赖注入(DI)和工厂模式相似的原因是因为它们是软件结构控制反转(IoC)的两种实现。简单地说,它们是针对同一问题的两种解决方案。

因此,要回答这个问题,Factory模式和DI之间的主要区别是如何获取对象引用。依赖注入的名称暗示该引用已注入或提供给您的代码。使用Factory模式,您的代码必须请求引用,以便您的代码访存该对象。两种实现都删除或分离了代码与代码所使用的对象引用的基础类或类型之间的链接。

值得注意的是,可以编写Factory模式(或者实际上是Abstract Factory模式,即返回新工厂并返回对象引用的工厂)是在运行时动态选择或链接到所请求对象的类型或类。这使它们与IoC的另一种实现的Service Locator模式非常相似(甚至比DI更是如此)。

工厂设计模式(在软件方面)非常古老,已经存在了一段时间。自从最近架构模式IoC流行以来,它正在复苏。

我想说到IoC设计模式:注入器正在注入,定位器正在定位,工厂已经重构。


3
这是最好的答案……其他答案要么不提及IoC,要么不认识DI是IoC的一种形式。
Hooman Bahreini

谢谢,当我第一次学习IOC时,我几乎大叫这只是工厂模式的另一种形式,事实证明它们真的彼此相似
Harvey Lin

40

有一些问题可以通过依赖注入轻松解决,而使用工厂套件很难解决。

一方面,控制反转和依赖项注入(IOC / DI)与另一方面,服务定位器或工厂套件(工厂)之间的某些区别是:

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

Factory提取了一些域对象和服务的构造。但是领域对象和服务仍然负责弄清楚如何构造自己以及如何获得它们所依赖的所有东西。所有这些“活动”依赖项都会一直过滤应用程序中的所有层。没有一个地方可以配置所有东西。


26

DI的一个缺点是它不能使用逻辑初始化对象。例如,当我需要创建一个具有随机名称和年龄的字符时,DI不是工厂模式的选择。使用工厂,我们可以轻松地封装对象创建过程中的随机算法,该算法支持一种称为“封装变化的内容”的设计模式。


22

生命周期管理是依赖容器除了实例化和注入之外还要承担的职责之一。容器在实例化后有时会保留对组件的引用这一事实是将其称为“容器”而不是工厂的原因。依赖注入容器通常仅保留其管理生命周期所需的对象的引用,或将其重复用于将来的注入,例如单例或重量级。当配置为每次调用容器创建某些组件的新实例时,容器通常只会忘记创建的对象。

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


15

我相信DI是工厂的一种抽象层,但它们也提供了抽象之外的好处。真正的工厂知道如何实例化单个类型并对其进行配置。好的DI层通过配置可以实例化和配置许多类型。

显然,对于具有几个简单类型的项目,这些项目在其构建中需要相对稳定的业务逻辑,因此工厂模式易于理解,实施且运作良好。

OTOH,如果您的项目包含许多您希望经常更改其实现的类型,则DI通过其配置为您提供了灵活性,使其可以在运行时执行此操作而不必重新编译工厂。


14

理论

有两点要考虑:

  1. 谁创造对象

    • [工厂]:您必须编写如何创建HOW对象。您有单独的Factory类,其中包含创建逻辑。
    • [依赖注入]:在实际情况下,是通过外部框架完成的(例如,在Java中为spring / ejb / guice)。注入“神奇地”发生,而无需显式创建新对象
  2. 它管理哪种对象:

    • [工厂]:通常负责创建有状态对象
    • [依赖项注入]更有可能创建无状态对象

实际示例如何在单个项目中同时使用工厂注入和依赖注入

  1. 我们想要建立什么

用于创建订单的应用程序模块,其中包含多个称为订单行的条目。

  1. 建筑

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

在此处输入图片说明

域对象可以是存储在数据库内部的对象。存储库(DAO)帮助从数据库中检索对象。服务向其他模块提供API。允许在order模块上进行操作

  1. 域层和工厂用法

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

现在是重要的设计部分。这个模块之外的模块是否应该自己创建和管理OrderLine?否。仅当您有与订单相关联时,订单行才应存在。最好将内部工具隐藏在外部类中。

但是,如何在不了解OrderLines的情况下创建Order?

想要创建新订单的人使用了OrderFactory(这将隐藏有关我们如何创建Order的事实的详细信息)。

在此处输入图片说明

那就是它在IDE中的外观。domain包外部的类将OrderFactory代替内部的构造函数Order

  1. 依赖注入依赖注入更常用于无状态层,例如存储库和服务。

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

在此处输入图片说明


你能澄清一下吗?[Factory]: Usually responsible for creation of stateful objects [Dependency Injections] More likely to create stateless objects我不明白为什么
Maksim Dmitriev

2
有状态对象可能位于应用程序的域层中,在该域中,您将数据映射到数据库时,通常不会在其中使用依赖项注入。Factory通常用于从复杂对象的树中创建AggregateRoot
Marcin Szymczak

12

我知道这个问题很旧,但是我想加五分钱,

我认为依赖注入(DI)在许多方面都类似于可配置的工厂模式(FP),从这种意义上讲,您可以使用DI进行的任何操作都可以在这种工厂中完成。

实际上,例如,如果您使用spring,则可以选择自动装配资源(DI)或执行以下操作:

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

然后使用该“ mb”实例执行任何操作。这不是对工厂的电话,它将返回您一个实例吗?

在大多数FP示例之间,我注意到的唯一真正的区别是,您可以配置xml或另一个类中的“ myBean”,并且框架将作为工厂工作,但除此之外,这是相同的,并且您可以肯定有一个Factory来读取配置文件或根据需要获取实现。

而且,如果您问我意见(我知道您没有),我相信DI可以做同样的事情,但是只会增加开发的复杂性,为什么呢?

好吧,一方面,要让您知道与DI自动装配的任何bean所使用的实现是什么,您必须去配置本身。

但是...那将使您不必知道所使用对象的实现的承诺又如何呢?pfft!认真吗?当您使用这样的方法时...编写实现的方法不一样吗?即使您不这样做,也几乎一直都在观察实现如何执行应做的工作?

最后一点是,DI框架向承诺多少,您将构建与之分离的事物并不依赖它们的类,这无关紧要;如果您使用的是框架,则必须构建它周围的所有内容,如果您必须改变方法或框架绝非易事...永远!...但是,由于您围绕该特定框架构建了所有内容,而不用担心什么是最适合您业务的解决方案,因此您在这样做时会遇到麻烦。

实际上,我可以看到的唯一用于FP或DI方法的实际业务应用程序是是否需要更改运行时使用的实现,但是至少我所知道的框架不允许您这样做,所以您必须离开开发时配置中的所有内容都很完美,如果需要,可以使用另一种方法。

因此,如果我有一个类在同一个应用程序的两个范围内执行不同的类(比方说,一家控股公司的两家公司),则必须配置该框架以创建两个不同的bean,并修改我的代码以使用每个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

无论如何,您都必须在应用程序中更改某些内容,无论是类还是配置文件,但是您都必须重新部署它。

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

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

这样,您可以设置工厂的代码,以根据登录的用户企业在运行时获得正确的实现??现在那将会有所帮助。您可以添加带有新类的新jar并甚至在运行时设置规则(或者,如果您将此选项保持打开状态,则添加新的配置文件),而无需更改现有类。这将是一个动态工厂!

不必为每个企业编写两个配置,甚至为每个企业编写两个不同的应用程序?

您可以告诉我,我不需要在运行时进行切换,因此可以配置应用程序,并且如果我继承该类或使用其他实现,则只需更改配置并重新部署即可。好的,这也可以在工厂完成。老实说,您执行了几次?也许只有当您的应用程序将在公司的其他地方使用,并且您要将代码传递给另一个团队时,他们才会做这样的事情。但是,这也可以通过工厂来完成,而对于动态工厂则更好!!

无论如何,如果有评论栏目供您杀死我。


3
太多的开发人员认为依赖注入框架的创建是为了带来新的东西。正如您很好地解释的那样,传统工厂(通常是抽象工厂)可以起到依赖反转的相同作用。反转与注入?对我来说,依赖关系注入框架的唯一“好处”是添加/更改依赖关系时,我们无需更改/重新编译代码(因为大多数情况下可以像Spring这样用XML进行配置)。为什么要避免重新编译?为了避免在更改依赖关系/工厂时出现一些潜在的人为错误。但是有了我们出色的IDE,重构就可以发挥出色了:)
Mik378

6

IOC是通过两种方式实现的概念。依赖关系创建和依赖关系注入,Factory / Abstract factory是依赖关系创建的示例。依赖注入是构造函数,setter和接口。IOC的核心是不依赖于具体类,而是定义方法的抽象(例如,接口/抽象类),并使用该抽象来调用具体类的方法。像Factory模式一样返回基类或接口。相似性依赖注入使用基类/接口来设置对象的值。


4

使用依赖注入,客户端不需要自己获取依赖关系,而是预先准备好所有依赖关系。

在工厂中,必须调用这些对象才能将生成的对象转移到需要它们的地方。

区别主要在于这一行,其中调用了工厂并获取了构造的对象。

但是对于工厂,您必须在需要此类对象的任何地方编写此1行。使用DI,您只需创建一次接线(用法和创建的对象之间的关系),然后在任何地方都依赖于对象的存在。另一方面,DI在准备方面通常需要做更多的工作(多少取决于框架)。


3

一读到关于DI的问题,我就遇到了同样的问题,并最终发表在这篇文章上。所以最后这就是我的理解,如果有误,请纠正我。

“很早以前,很少有王国拥有自己的理事机构根据自己的书面规则来控制和决策。后来成立了一个大政府,消除了所有这些具有一套规则(宪法)并通过法院实施的小理事机构”

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

大政府是“依赖注入者”。


1
那么,什么时候可以将以前的小政府变成现在的大政府呢?通过什么措施?
囚犯零

3

您可以查看此链接,以比较实际示例中的两种(和其他)方法。

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

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


3

这里的大多数答案都说明了两者的概念差异和实现细节。但是,我找不到关于IMO最重要且OP询问的应用程序差异的解释。所以让我重新打开这个话题...

曾经有人告诉我,您如何使用它与众不同!

究竟。在90%的情况下,您可以使用Factory或DI获取对象引用,并且通常以后者为最终对象。在另外10%的情况下,使用Factory 只是正确的方法。这些情况包括在运行时参数中通过变量获取对象。像这样:

IWebClient client = factoryWithCache.GetWebClient(url: "stackoverflow.com",
        useCookies: false, connectionTimeout: 120);

在这种情况下,client无法从DI 获取(或至少需要一些难看的解决方法)。因此作为决策的一般规则:如果可以在没有运行时计算的参数的情况下获得依赖关系,则首选DI,否则使用Factory。


2

我相信DI是一种配置或实例化bean的方法。DI可以通过构造器,setter-getter等多种方式完成。

工厂模式只是实例化bean的另一种方法。此模式主要用于必须使用工厂设计模式创建对象的情况,因为在使用此模式时,您无需配置Bean的属性,而仅实例化该对象。

检查此链接:依赖注入


2

比诺伊

我认为您不必选择一个。

将依赖类或接口移至类构造函数或设置器的操作遵循DI模式。传递给构造函数或集合的对象可以使用Factory来实现。

什么时候使用?使用开发人员平台中的一个或多个模式。他们觉得最舒适,最容易理解的是什么。


2

我相信,三个重要方面决定着对象及其用法:
1. 实例化(类的初始化和初始化,如果有的话)。
2. 在需要的地方注入(这样创建的实例)。
3. (如此创建的实例的)生命周期管理

使用Factory模式,可以实现第一个方面(实例化),但是其余两个方面是有问题的。使用其他实例的类必须对工厂进行硬编码(而不是创建实例),这会阻碍松散的耦合能力。此外,生命周期管理在大型应用程序中,在多个地方使用工厂是一个挑战(特别是,如果工厂不管理实例返回的实例的生命周期,它将变得很丑)。

另一方面,使用DI(具有IoC模式),所有3个代码都将抽象到代码外部(到DI容器中),并且托管Bean不需要这种复杂性。松散耦合可以舒适地安静地实现非常重要的建筑目标。另一个重要的体系结构目标是,关注点分离比工厂要好得多。

尽管工厂可能适用于小型应用,但大型工厂最好选择DI,而不是工厂。


1

我的想法:

依赖注入:将协作者作为参数传递给构造函数。依赖注入框架(Dependency Injection Framework):一种通用且可配置的工厂,用于创建要作为参数传递给构造函数的对象。


1
对,就是这样。与该定义相反,在此问答中几乎所有提及依赖注入(DI)的术语都被滥用。依赖项注入容器(DIC)是最常见的框架,用作创建对象的通用且可配置的工厂。
CXJ

1

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

这完全取决于您的要求。如果您需要在应用程序中实现工厂模式,那么很可能会通过无数种注入框架实现之一来满足您的要求。

如果任何第三方框架都无法满足您的要求,则仅应推出自己的解决方案。您编写的代码越多,必须维护的代码就越多。代码是负债而不是资产。

关于应该使用哪种实现的争论并不像了解应用程序的体系结构需求那样重要。


1

工厂设计模式

工厂设计模式的特点是

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

当您质疑自己时,您可以观察到以下几点:

  • 工厂何时会为实现类创建对象-运行时还是编译时?
  • 如果要在运行时切换实现怎么办?- 不可能

这些通过依赖注入来处理。

依赖注入

您可以有不同的方式来注入依赖项。为简单起见,让我们进行接口注入

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

从而消除了静态实例化。

例:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

1

从面值看,它们看起来一样

用非常简单的术语来说,“工厂模式”即“创建模式”有助于创建一个对象-“定义用于创建对象的接口”。如果我们有一个键值类型的对象池(例如字典),将键传递给工厂(我指的是简单工厂模式),则可以解析类型。任务完成!另一方面,依赖注入框架(例如Structure Map,Ninject,Unity ...等)似乎在做同样的事情。

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

从架构的角度看,它是一个绑定层,并且“不要重新发明轮子”。

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

除了工厂提供的“ GetType / Create”之外,通常我们还需要更多功能(使用XML定义依赖项,模拟和单元测试等功能)。由于您已经参考了“结构图”,因此请查看“ 结构图”功能列表。显然,这不仅仅是解决简单的对象映射。不要重新发明轮子!

如果您只有锤子,那么一切看起来就像钉子

根据您的要求和您构建的应用程序类型,您需要做出选择。如果它只有几个项目(可能是一个或两个..),并且涉及的依赖很少,则可以选择一种更简单的方法。就像通过简单的1或2个数据库调用使用Entity Framework来使用ADO .Net数据访问一样,在这种情况下,引入EF可能会显得过分。

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


1

在工厂中,您可以对相关接口进行分组,因此,如果可以在工厂中对传递的参数进行分组,那么constructor overinjection查看此代码* 也是一个很好的解决方案*):

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传递的许多参数中看到,是的,您可以看到,constructor overinjection但这就是DI的工作方式。而且没什么错CustomerController

*)代码来自nopCommerce。


1

简而言之,依赖注入与工厂方法分别意味着推入与拉动机制。

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

推动机构:根组件可以与所有从属组件一起配置在一个位置,从而提高了维护成本和松耦合。

使用Factory方法时,责任仍然属于类(尽管是间接地)以创建新对象,在该对象中,与依赖项注入一样,责任被外包(尽管以泄漏抽象为代价)


0

我认为这些是正交的,可以一起使用。让我向您展示一个我最近在工作中遇到的示例:

我们在Java中使用Spring框架进行DI。单例类(Parent)必须实例化另一个类(Child)的新对象,并且这些对象具有复杂的协作者:

@Component
class Parent {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    void method(int p) {
        Child c = new Child(dep1, dep2, ..., depN, p);
        // ...
    }
}

在这个例子中,Parent具有接收DepX唯一实例,以将它们传递到Child构造函数。问题:

  1. ParentChild比应该更多的知识
  2. Parent 有更多的合作者
  3. 添加依赖项Child涉及更改Parent

这是我意识到一个Factory完美适合的地方:

  1. 它隐藏了Child该类的所有参数,除了真正的参数,Parent
  2. 它封装了创建的知识,这些知识Child可以集中在DI配置中。

这是简化的Parent类和ChildFactory类:

@Component
class Parent {
    // ...
    @Autowired
    Parent(ChildFactory childFactory) {
        this.childFactory = childFactory;
    }

    void method(int p) {
        Child c = childFactory.newChild(p);
        // ...
    }
}

@Component
class ChildFactory {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
        // ...
        this.depN = depN;
    }

    Child newChild(int p) {
        return new Child(dep1, dep2, ..., depN, p);
    }
}

0

当您完全知道此时需要哪种类型的对象时,可以使用依赖项注入。在使用工厂模式的情况下,您只是将创建对象的过程委托给工厂,因为您不清楚所需的对象的确切类型。


-1

我都使用这两种方法来创建“控制反转”策略,以使需要在我之后进行维护的开发人员更具可读性。

我使用Factory创建不同的Layer对象(业务,数据访问)。

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

另一个开发人员将看到这种情况,并且在创建业务层对象时,他会在BusinessFactory中查找,并且Intellisense为开发人员提供了所有可能的业务层创建。不必玩游戏,找到我要创建的界面。

此结构已经是控制反转。我不再负责创建特定对象。但是您仍然需要确保依赖注入能够轻松地进行更改。创建您自己的自定义Dependency Injection很荒谬,因此我使用Unity。在CreateCarBusiness()中,我要求Unity解析属于该类的类以及它的生命周期。

所以我的代码工厂依赖注入结构为:

public static class BusinessFactory
{
    public static ICarBusiness CreateCarBusiness()
    {
       return Container.Resolve<ICarBusiness>();
    }
}

现在我都受益。对于其他开发人员,我的代码对于我使用的对象的范围也更具可读性,而不是构造函数依赖注入(Instructor Dependency Injection),后者只是说创建类时每个对象都可用。

创建单元测试时,我用它来将数据库数据访问更改为自定义编码的数据访问层。我不希望我的单元测试与数据库,Web服务器,电子邮件服务器等进行通信。它们需要测试我的业务层,因为这就是智能所在。


-4

我认为,在以下情况下使用依赖注入会更好:1.将代码部署在小分区中,因为它可以很好地解耦一个大代码。2.可测试性是DI可以使用的情况之一,因为您可以轻松模拟非解耦的对象。使用接口,您可以轻松地模拟和测试每个对象。3.您可以同时修改程序的每个部分,而无需对程序的另一部分进行编码,因为它是松散耦合的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.