540

引用的大多数使用依赖注入的示例,我们也可以使用工厂模式来解决。看起来在使用/设计方面,依赖注入和工厂之间的区别是模糊的或薄的。

曾经有人告诉我,你如何使用它会有所作为!

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

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

4

30 回答 30

307

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

于 2009-02-17T17:14:02.433 回答
235

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

于 2009-02-17T17:10:28.777 回答
218

依赖注入

汽车不需要实例化零件本身,而是要求其运行所需的零件。

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 - 这稍微好一点,因为现在我们知道我们需要一辆汽车,因为我们经过了一家汽车工厂。但这一次我们传递的太多了,因为方法实际上需要的只是一辆汽车。当汽车可以在方法之外制造并通过时,我们正在通过工厂来制造汽车。

示例#3 - 这是理想的,因为该方法准确地要求需要什么。不要太多或太少。我不必为了创建MockCars而编写MockCarFactory,我可以直接将mock传入。它是直接的,界面不会说谎。

Misko Hevery 的 Google 技术讲座非常棒,是我从中得出示例的基础。http://www.youtube.com/watch?v=XcT4yYu_TTs

于 2013-06-04T06:06:39.140 回答
66

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

所以要回答这个问题,工厂模式和 DI 之间的主要区别在于如何获得对象引用。顾名思义,依赖注入意味着引用被注入或提供给您的代码。使用工厂模式,您的代码必须请求引用,以便您的代码获取对象。两种实现都删除或解耦代码与代码使用的对象引用的底层类或类型之间的链接。

值得注意的是,工厂模式(或者实际上是返回对象引用的新工厂的工厂的抽象工厂模式)可以编写为动态选择或链接到运行时请求的对象的类型或类。这使得它们与服务定位器模式非常相似(甚至比 DI 更相似),服务定位器模式是 IoC 的另一种实现。

工厂设计模式相当古老(就软件而言)并且已经存在了一段时间。自从最近流行的架构模式 IoC 以来,它正在复苏。

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

于 2014-03-13T16:04:40.410 回答
40

有些问题很容易通过依赖注入解决,而用一套工厂则不容易解决。

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

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

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

于 2009-02-17T17:16:50.463 回答
27

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

于 2012-05-01T09:24:19.357 回答
22

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

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

于 2010-12-20T20:09:28.193 回答
17

理论

有两点需要考虑:

  1. 谁创建对象

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

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

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

  1. 我们想要建造的

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

  1. 建筑学

假设我们要创建以下层架构:

在此处输入图像描述

域对象可能是存储在数据库中的对象。存储库 (DAO) 有助于从数据库中检索对象。Service 为其他模块提供 API。允许对order模块进行操作

  1. 领域层和工厂的使用

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

现在是重要的设计部分。这个模块之外的模块是否应该自行创建和管理 OrderLines?不,只有当您有与之关联的订单时,订单行才应该存在。如果您可以将内部实现隐藏到外部类,那将是最好的。

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

工厂

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

在此处输入图像描述

这就是它在 IDE 中的外观。包外的类domain将使用OrderFactory而不是内部的构造函数Order

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

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

在此处输入图像描述

于 2017-05-29T12:59:27.787 回答
16

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

显然,对于一个类型比较简单的项目,在构建时需要相对稳定的业务逻辑,工厂模式理解简单,实现简单,运行良好。

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

于 2009-02-17T17:13:04.323 回答
13

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

我认为依赖注入(DI)在很多方面就像可配置的工厂模式(FP),从这个意义上说,你可以用 DI 做的任何事情,你都可以用这样的工厂来做。

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

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

然后使用那个 'mb' 实例做任何事情。这不是对工厂的调用,它会返回一个实例吗?

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

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

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

但是......关于你不必知道你正在使用的对象的实现的承诺呢?噗!严重地?当您使用这样的方法时……您与编写实现的人不一样吗?即使您不这样做,您是否几乎一直在查看实现如何完成它应该做的事情?

最后一件事,无论 DI 框架向您承诺多少,您将构建与它分离的东西,而不依赖于它们的类,如果您使用的是框架,那么您将围绕它构建所有东西,如果您必须改变方法或框架这不是一件容易的事......永远!...但是,由于您围绕该特定框架构建所有内容,而不是担心什么是您业务的最佳解决方案,那么在这样做时您将面临一个问题。

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

所以,如果我有一个类在同一个应用程序的两个范围内执行不同的操作(比如说,两个控股公司),我必须配置框架来创建两个不同的 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,甚至在运行时也可以设置规则(或者如果您打开此选项,则添加一个新的配置文件),而不更改现有类。这将是一个动态工厂!

这不会比必须为每个企业编写两个配置,甚至可能为每个企业编写两个不同的应用程序更有帮助吗?

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

无论如何,评论区如果开放让你杀了我。

于 2012-09-13T05:02:57.967 回答
6

IOC 是一个通过两种方式实现的概念。依赖创建和依赖注入,工厂/抽象工厂是依赖创建的例子。依赖注入是构造器、设置器和接口。IOC 的核心是不依赖于具体类,而是定义方法的抽象(比如接口/抽象类),并使用该抽象来调用具体类的方法。像工厂模式一样返回基类或接口。类似地,依赖注入使用基类/接口来设置对象的值。

于 2011-08-28T08:22:42.247 回答
4

使用依赖注入,客户端不需要自己获取依赖,所有这些都是事先准备好的。

对于工厂,必须有人调用它们才能将生成的对象送到需要它们的地方。

不同之处主要在于调用工厂和获取构造对象的这一行。

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

于 2009-02-17T17:12:34.200 回答
3

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

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

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

于 2015-01-14T15:54:07.353 回答
3

当我读到关于 DI 并最终看到这篇文章时,我就有了同样的问题。所以最后这就是我所理解的,但如果错了请纠正我。

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

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

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

于 2010-12-20T22:23:43.110 回答
3

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

曾经有人告诉我,你如何使用它会有所作为!

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

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

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

于 2017-11-21T10:35:51.103 回答
3

使用工厂,您可以对相关接口进行分组,因此如果传递的参数可以在工厂中分组,那么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。

于 2017-11-28T14:51:04.173 回答
3

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

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

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

使用工厂方法的责任仍然在于类(尽管是间接的)来创建新对象,而依赖注入则将责任外包(但以泄漏抽象为代价)

于 2018-02-13T07:41:52.527 回答
2

比诺伊,

我认为您不必选择其中之一。

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

什么时候使用?使用开发人员驾驶室中的一种或多种模式。他们觉得什么最舒服,最容易理解。

于 2009-02-17T19:02:07.887 回答
2

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

工厂模式只是实例化 bean 的另一种方式。这种模式主要在你必须使用工厂设计模式创建对象时使用,因为在使用这种模式时你不配置 bean 的属性,只实例化对象。

检查此链接:依赖注入

于 2009-02-17T18:01:17.190 回答
2

You use dependency injection when you exactly know what type of objects you require at this point of time. While in case of factory pattern, you just delegate the process of creating objects to factory as you are unclear of what exact type of objects you require.

于 2020-05-04T20:28:50.807 回答
2

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

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

另一方面,使用 DI(IoC 模式),所有 3 个都在代码之外(到 DI 容器)抽象出来,托管 bean 不需要这种复杂性。松耦合,一个非常重要的架构目标可以安静舒适地实现。另一个重要的架构目标,可以比工厂更好地实现 关注点分离。

虽然工厂可能适合小型应用程序,但大型应用程序最好选择 DI 而不是工厂。

于 2017-04-28T05:24:19.707 回答
1

My thoughts:

Dependecy Injection: pass collaborators as parameters to the constructors. Dependency Injection Framework: a generic and configurable factory to create the objects to pass as parameters to the constructors.

于 2009-10-28T01:22:39.863 回答
1

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

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

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

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

于 2013-06-17T02:23:11.207 回答
1

工厂设计模式

工厂设计模式的特点是

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

当您如下提问时,您可以观察到几件事

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

这些由依赖注入处理。

依赖注入

你可以有不同的方式来注入依赖。为简单起见,让我们使用接口注入

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

从而消除了静态实例化。

例子:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}
于 2014-09-24T08:20:35.970 回答
1

从表面上看,它们看起来一样

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

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

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

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

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

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

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

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

于 2016-03-31T01:07:59.457 回答
1

我认为这些是正交的,可以一起使用。让我给你看一个我最近在工作中遇到的例子:

我们在 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. Parent拥有Child比应有的更多的知识
  2. Parent拥有比应有的更多的合作者
  3. 添加依赖Child项涉及更改Parent

这是我意识到 aFactory非常适合这里的时候:

  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);
    }
}
于 2018-04-16T20:47:19.467 回答
0

[Factory] ​​-> 有一个类根据请求参数创建类。毕竟,“工厂”也在现实世界中为您制造“对象”。您可以要求您的汽车供应商工厂制造(免费:))特斯拉版本。1给你。

[DI] -> 有一个(服务)容器存储接口(契约类)。你不关心创建对象。您只需询问某人/某处来实现它,细节和其他东西对您、呼叫者或消费者都无关紧要。

DI 是 SOLID 原则中“D”的基础。

于 2021-11-09T14:35:24.877 回答
0

DI 为您提供组合根,它是用于连接对象图的单个集中位置。这往往会使对象依赖关系非常明确,因为对象准确地要求它们需要什么,并且只有一个地方可以得到它。

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

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

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

GoF 提到了更新抽象工厂的困难。他们的部分解释在此处的答案中被引用。将 DI 与工厂进行对比也与问题有很多共同点,ServiceLocator 是反模式吗?

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

图表有助于说明差异。 DI 与工厂

于 2020-09-30T19:04:04.733 回答
-1

我使用两者来创建一个控制反转策略,对于需要在我之后维护它的开发人员来说具有更高的可读性。

我使用工厂来创建不同的图层对象(业务、数据访问)。

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

另一位开发人员将看到这一点,并在创建业务层对象时查看 BusinessFactory 和 Intellisense 为开发人员提供了所有可能的业务层来创建。不用玩游戏,找到我要创建的界面。

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

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

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

现在我得到了两者的好处。对于其他开发人员来说,我的代码对于我使用的对象的范围也更具可读性,而不是构造函数依赖注入,它只是说每个对象在创建类时都是可用的。

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

于 2018-06-25T20:49:38.707 回答
-4

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

于 2014-12-22T19:21:33.563 回答