已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,

什么是依赖注入,何时/为什么应该或不应该使用它?


当前回答

依赖注入是一种实践,它使解耦的组件与它们的一些依赖不可知,这遵循SOLID准则

依赖反转原则:一个人应该“依赖于抽象,而不是结核。

依赖注入的更好实现是Composition Root设计模式,因为它允许组件与依赖注入容器分离。

我再次推荐这篇关于作文根的伟大文章http://blog.ploeh.dk/2011/07/28/CompositionRoot/作者:Mark Seemann

本文的要点如下:

合成根是应用程序中的(最好)唯一位置其中模块被组合在一起。

...

只有应用程序应该具有合成根。图书馆和框架不应该。

...

DI容器只能从合成根引用。所有其他模块都不应引用容器。

Di Ninja(依赖注入框架)的文档是一个很好的例子,可以演示组合根和依赖注入的原理是如何工作的。https://github.com/di-ninja/di-ninja正如我所知,是javascript中唯一实现Composition Root设计模式的DiC。

其他回答

来自Book Apress.Spring.Persistence.with.HHibernate,2010年10月

依赖注入的目的是将解决应用程序业务中的外部软件组件逻辑。如果没有依赖注入访问所需的服务可能会与组件的密码这不仅增加了出错的可能性,还增加了代码膨胀,并放大了维护复杂性;它耦合组件更紧密地结合在一起,使得在重构或测试。

任何非平凡的应用程序都由两个或多个类组成,它们相互协作以执行某些业务逻辑。传统上,每个对象都负责获取自己对与其协作的对象(其依赖关系)的引用。在应用DI时,对象在创建时由协调系统中每个对象的某个外部实体赋予其依赖性。换句话说,依赖项被注入到对象中。

有关详细信息,请参阅此处输入链接描述

我们可以实现依赖注入来了解它:

class Injector {
  constructor() {
    this.dependencies = {};
    this.register = (key, value) => {
      this.dependencies[key] = value;
    };
  }
  resolve(...args) {
    let func = null;
    let deps = null;
    let scope = null;
    const self = this;
    if (typeof args[0] === 'string') {
      func = args[1];
      deps = args[0].replace(/ /g, '').split(',');
      scope = args[2] || {};
    } else {
      func = args[0];
      deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
      scope = args[1] || {};
    }
    return (...args) => {
      func.apply(scope || {}, deps.map(dep => self.dependencies[dep] && dep != '' ? self.dependencies[dep] : args.shift()));
    }
  }
}

injector = new Injector();

injector.register('module1', () => { console.log('hello') });
injector.register('module2', () => { console.log('world') });

var doSomething1 = injector.resolve(function (module1, module2, other) {
  module1();
  module2();
  console.log(other);
});
doSomething1("Other");

console.log('--------')

var doSomething2 = injector.resolve('module1,module2,', function (a, b, c) {
  a();
  b();
  console.log(c);
});
doSomething2("Other");

以上是javascript的实现

这意味着对象应该只具有完成其工作所需的依赖项,并且依赖项应该很少。此外,如果可能的话,对象的依赖关系应该是接口,而不是“具体”对象。(具体对象是用关键字new创建的任何对象。)松散耦合促进了更高的可重用性,更容易维护,并允许您轻松地提供“模拟”对象来代替昂贵的服务。

“依赖注入”(DI)也称为“控制反转”(IoC),可以用作鼓励这种松散耦合的技术。

实施DI有两种主要方法:

构造函数注入设值注入

构造函数注入

这是一种将对象依赖关系传递给构造函数的技术。

注意,构造函数接受接口而不是具体对象。此外,请注意,如果orderDao参数为空,则会引发异常。这强调了接受有效依赖的重要性。在我看来,构造函数注入是赋予对象依赖关系的首选机制。在调用对象时,开发人员很清楚需要向“Person”对象提供哪些依赖关系才能正确执行。

沉淀剂注入

但是考虑下面的例子……假设您有一个类,它有十个没有依赖关系的方法,但是您要添加一个新方法,它确实依赖于IDAO。您可以将构造函数更改为使用构造函数注入,但这可能会迫使您更改所有的构造函数调用。或者,您可以添加一个新的构造函数来获取依赖项,但是开发人员如何轻松地知道何时使用一个构造函数而不是另一个构造函数。最后,如果依赖项的创建成本很高,为什么要创建它并传递给构造函数,因为它可能很少使用?“Setter Injection”是另一种DI技术,可用于此类情况。

Setter注入不会强制将依赖项传递给构造函数。相反,依赖项被设置到需要的对象公开的公共财产上。正如前面所暗示的,这样做的主要动机包括:

支持依赖注入而无需修改遗留类的构造函数。允许在需要时尽可能晚地创建昂贵的资源或服务。

下面是上述代码的示例:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;

依赖注入是一种实践,其中对象的设计方式是从其他代码段接收对象实例,而不是在内部构造它们。这意味着可以在不更改代码的情况下替换实现对象所需接口的任何对象,这简化了测试,并改进了去耦。

例如,考虑这些类:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

在本例中,PersonService::addManager和PersonService::removeManager的实现需要GroupMembershipService的实例才能完成其工作。如果没有依赖注入,传统的方法是在PersonService的构造函数中实例化一个新的GroupMembershipService,并在两个函数中使用该实例属性。但是,如果GroupMembershipService的构造函数有多个它需要的东西,或者更糟的是,需要在GroupMembershipServices上调用一些初始化“setter”,代码增长相当快,PersonService现在不仅依赖于GroupMembershipService,还依赖于GroupMembershipService所依赖的所有其他东西。此外,与GroupMembershipService的链接被硬编码到PersonService中,这意味着您不能为了测试目的而“虚拟”GroupMembershipServices,或者在应用程序的不同部分使用策略模式。

使用依赖注入,而不是在PersonService中实例化GroupMembershipService,您可以将其传递给PersonService构造函数,或者添加Property(getter和setter)来设置其本地实例,并与他们合作。这也意味着,任何属于GroupMembershipService的子类或实现GroupMembershipServices接口的内容都可以“注入”到PersonService中,PersonService不需要知道更改。