依赖注入与工厂模式

对于使用依赖注入引用的大多数示例,我们也可以使用工厂模式来解决 . 看起来在使用/设计时,依赖注入和工厂之间的差异是模糊的还是薄的 .

一旦有人告诉我它如何使用它会产生影响!

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

任何人都可以告诉我他们之间的区别是什么,在哪里使用什么,这里最好的做法是什么?

回答(27)

3 years ago

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

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

检查此链接:Dependency Injection

3 years ago

存在易于通过依赖注入解决的问题,这些问题不容易通过一组工厂解决 .

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

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

工厂抽象出您的域对象和服务的一些构造 . 但是域对象和服务仍然负责确定如何构建自己以及如何获得他们所依赖的所有东西 . 所有这些“活动”依赖项都会过滤到应用程序中的所有层 . 没有一个地方可以配置一切 .

3 years ago

我相信,3个重要方面管理对象及其用法:

  1. Instantiation (一个类,如果有的话,还有初始化) .
  2. Injection (如此创建的实例)所需的位置 .
  3. Life cycle management (如此创建的实例) .

使用工厂模式,实现了第一个方面(实例化),但剩下的两个方面是有问题的 . 使用其他实例的类必须 hardcode the factories (而不是创建实例),这会阻碍松耦合能力 . 此外, life cycle management 实例在大型应用程序中成为挑战,其中工厂在多个地方使用(特别是,如果工厂不管理它返回的实例的生命周期,则会变得难看) .

另一方面,使用DI(IoC模式),所有3个都在代码之外被抽象(到DI容器),并且托管bean不需要这种复杂性 . Loose Coupling ,一个非常重要的建筑目标可以安静舒适地实现 . 另一个重要的建筑目标, separation of concerns 可以比工厂更好地实现 .

虽然工厂可能适合小型应用,但大型工厂选择DI比工厂更好 .

3 years ago

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

这一切都取决于您的要求 . 如果您需要在应用程序中实现工厂模式,那么很有可能您的需求将通过其中的众多注入框架实现之一来满足 .

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

您应该使用哪些实现的参数并不像了解应用程序的体系结构需求那样重要 .

3 years ago

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

3 years ago

在我看来,使用依赖注入要好得多:1 . 在小分区中部署代码,因为它可以很好地处理一个大代码的解耦 . 2.可测试性是DI可以使用的情况之一,因为您可以轻松地模拟非解耦对象 . 随着使用您可以轻松地模拟和测试每个对象的接口 . 3.你可以同时修改程序的每个部分,而不需要编码它的其他部分,因为它松散地分离 .

3 years ago

From a face value they look same

简单来说,工厂模式,创建模式有助于创建一个对象 - “定义创建对象的接口” . 如果我们有一个键值对象池(例如Dictionary),将密钥传递给Factory(我指的是简单工厂模式),你可以解析Type . 任务完成!另一方面,依赖注入框架(例如Structure Map,Ninject,Unity ...等)似乎也在做同样的事情 .

But... "Don't reinvent the wheel"

从建筑的角度来看,它是一个绑定层和“不要重新发明轮子” .

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

除了来自工厂的"GetType/Create"之外,我们通常需要更多功能(能够使用XML来定义依赖关系,模拟和单元测试等) . 由于您引用了结构图,请查看Structure Map feature list . 它重新发明轮子!

If all you have is a hammer, everything looks like a nail

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

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

3 years ago

我一看完DI就得到了同样的问题,最后在这篇文章中结束了 . 所以最后这就是我的理解,但如果我错了,请纠正我 .

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

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

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

3 years ago

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

3 years ago

我使用 both 来创建一个Inversion of Control策略,为需要在我之后维护它的开发人员提供更多的可读性 .

我使用Factory来创建不同的Layer对象(Business,Data Access) .

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

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

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

所以我的代码Factory Dependency Injection结构是:

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

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

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

3 years ago

Binoj,

我不认为你必须选择一个而不是另一个 .

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

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

3 years ago

Factory Design Pattern

工厂设计模式的特点是

  • 一个接口

  • 实施班

  • 一家工厂

您当你质疑自己时,可以观察到一些事情

  • 工厂何时为实现类创建对象 - 运行时或 compile time?

  • 如果要在运行时切换实现,该怎么办? - Not possible

这些由依赖注入处理 .

Dependency injection

您可以通过不同的方式注入依赖项 . 为简单起见,请使用Interface Injection

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

从而消除了静态实例化 .

例:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

3 years ago

理论

有两个要点需要考虑:

  • 谁创建了对象

  • [工厂]:你必须写HOW对象应该被创建 . 您有单独的Factory类,其中包含创建逻辑 .

  • [依赖注入]:在实际情况下由外部框架完成(例如在Java中将是spring / ejb / guice) . 注入发生"magically"没有明确创建新对象

  • 它管理的是什么类型的对象:

  • [工厂]:通常负责创建有状态对象

  • [依赖注入]更有可能创建无状态对象


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

  • 我们想要 Build 什么

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

  • 建筑

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

enter image description here

域对象可以是存储在数据库内的对象 . 存储库(DAO)帮助从数据库中检索对象 . Service为其他模块提供API . 在 order 模块上进行操作

  • 域层和工厂的使用

将在数据库中的实体是Order和OrderLine . 订单可以有多个OrderLines .
Relationship between Order and OrderLine

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

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

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

enter image description here

这就是IDE内部的样子 . domain 包之外的类将使用 OrderFactory 而不是 Order 中的构造函数

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

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

enter image description here

3 years ago

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

我们在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 构造函数 . 这个问题:

  • ParentChild 有更多的了解

  • Parent 有更多的合作者

  • 将依赖项添加到 Child 涉及更改 Parent

这是我意识到 Factory 完全适合这里的时候:

  • 它隐藏了除 Child 类的所有参数之外的所有参数,如 Parent 所示

  • 它封装了创建 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);
    }
}

3 years ago

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

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

3 years ago

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

3 years ago

这里的大多数答案解释了两者的概念差异和实现细节 . 但是我无法找到关于IMO最重要且OP询问的应用差异的解释 . 那么让我重新开启这个话题......

一旦有人告诉我它如何使用它会产生影响!

究竟 . 在90%的情况下你可以使用Factory或DI获取对象引用,通常最终使用后者 . 在使用Factory的另外10%的情况下是 only correct way . 这些情况包括在运行时参数通过变量获取对象 . 像这样:

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

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

3 years ago

Dependency Injection

而不是将零件本身实例化为汽车 asks ,用于它需要运行的零件 .

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

Factory

将碎片放在一起以制作完整的物体并隐藏来自呼叫者的具体类型 .

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

Result

如您所见,Factories和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 - 这是最糟糕的,因为它完全隐藏了依赖性 . 如果你把方法视为一个黑盒子,你根本不知道它需要一辆车 .

Example #2 - 这有点好,因为现在我们知道自从我们进入汽车工厂以来我们需要一辆汽车 . 但是这次我们传球太多了,因为所有方法实际需要的都是汽车 . 当汽车可以在方法之外建造并通过时,我们正在通过一个工厂来建造汽车 .

Example #3 - 这是理想的,因为该方法要求 exactly 它需要什么 . 不是太多或太少 . 我不是说谎 .

Misko Hevery的Google Tech Talk非常棒,是我从中得到的例子的基础 . http://www.youtube.com/watch?v=XcT4yYu_TTs

3 years ago

我知道这个问题已经过时了,但我想加上我的五美分,

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

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

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

然后使用'mb'实例做任何事情 . 这不是一个工厂的调用会返回你的实例?

我注意到大多数FP示例之间唯一真正的区别是你可以在xml或另一个类中配置“myBean”,并且框架将作为工厂工作,但除此之外是同样的事情,而你肯定有一个工厂可以读取配置文件或获得所需的实现 .

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

好吧,首先,要知道您使用DI自动装配的任何bean所使用的实现是什么,您必须转到配置本身 .

但是......那个你不必知道你正在使用的对象的实现的承诺怎么样? pfft!当真?当你使用这样的方法......你是不是写了实现?即使你不这样做,你几乎不是一直在看实现如何做它应该做的事情?

至于最后一件事, it doesn't matter how much a DI framework promises 你,你将从它构建东西 decoupled ,没有依赖于它们的类,如果你使用的是一个框架,你构建它的所有东西, if you have to change the approach or the framework it will not be an easy task... EVER! ...但是,因为你围绕那个特定的框架构建了一切而不是担心什么是你的业务的最佳解决方案,那么你在这样做时将面临一个biiig问题 .

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

所以,如果我有一个在同一个应用程序中的两个作用域中执行不同的类(比方说,两个持有的公司),我必须配置框架来创建两个不同的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 years ago

依赖注入(DI)和工厂模式相似的原因是因为它们是控制反转(IoC)的两种实现,它是一种软件体系结构 . 简而言之,它们是解决同一问题的两种解决方案 .

因此,为了回答这个问题,工厂模式和DI之间的主要区别在于如何获得对象引用 . 使用依赖注入,顾名思义就是将引用注入或提供给您的代码 . 使用Factory模式,您的代码必须请求引用,以便您的代码获取对象 . 两种实现都删除或解耦代码与代码使用的对象引用的基础类或类型之间的链接 .

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

工厂设计模式相当陈旧(就软件而言)并且已经存在了一段时间 . 自从最近建筑模式IoC的普及以来,它正在复苏 .

我想当涉及到IoC设计模式时:注入器是注入的,定位器是定位的,工厂已被重构 .

3 years ago

我的想法:

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

3 years ago

使用工厂,您可以对相关接口进行分组,因此如果传递的参数可以在工厂中分组,那么它也是一个很好的解决方案 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 .

3 years ago

您可以查看this link,以比较实际示例中的两种(和其他)方法 .

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

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

3 years ago

通过依赖注入,客户端不需要自己获取它的依赖关系,它们都是事先准备好的 .

对于工厂,有人必须调用那些将生成的对象转移到需要它们的地方 .

不同之处主要在于这一行,其中调用工厂并获取构造的对象 .

但是对于工厂,你必须在任何地方写这一行你需要这样一个对象 . 使用DI,您只需创建一次布线(使用和创建对象之间的关系),然后再依赖于对象的存在 . 另一方面,DI通常需要更多(在多大程度上取决于框架)在准备方面的工作 .

3 years ago

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

3 years ago

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

显然,对于具有一些简单类型的项目,在构造中需要相对稳定的业务逻辑,工厂模式易于理解,实现并且运行良好 .

OTOH,如果您的项目包含许多类型,您希望经常更改它们的实现,DI可以通过其配置灵活地在运行时执行此操作,而无需重新编译工厂 .

3 years ago

In simple terms Dependency Injection vs Factory method implies push vs pull mechanism respectively.

使用pull机制:class间接依赖于Factory Method反过来依赖于具体的类 .

使用推送机制:根组件可以在一个位置配置所有相关组件,从而促进高维护和松散耦合 .

使用Factory方法的责任仍然在于类(尽管间接地)来创建新对象,其中依赖注入将责任外包(尽管以泄漏抽象为代价)