什么是依赖注入?

时间:2020-03-06 14:41:52  来源:igfitidea点击:

已经发布了几个问题,其中包含有关依赖项注入的特定问题,例如何时使用它以及支持它的框架。然而,

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

解决方案

基本上,不是让对象创建依赖关系或者让工厂对象为其创建依赖关系,而是将所需的依赖关系从外部传递给对象,从而使它成为其他人的问题。这个"某人"是依赖图上的一个对象,或者是构建依赖图的依赖注入器(框架)。我在这里使用的依赖项是当前对象需要持有引用的任何其他对象。

依赖项注入的主要优点之一是它可以使测试变得更加容易。假设我们有一个在其构造函数中执行以下操作的对象:

public SomeClass() {
    myObject = Factory.getObject();
}

当我们要做的只是在SomeClass上运行某些单元测试时,这可能会很麻烦,尤其是当myObject是可以进行复杂磁盘或者网络访问的东西时。因此,现在我们正在研究模拟myObject,但又以某种方式拦截了工厂调用。难的。而是将对象作为参数传递给构造函数。现在,我们已将问题移至其他位置,但是测试变得更加容易。只需创建一个虚拟myObject并将其传递进去即可。构造函数现在看起来像:

public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

这是通过构造函数进行依赖注入的一种方式。几种机制是可能的。

  • 如评论中所述,一种常见的替代方法是定义空操作构造函数,并通过属性设置器(h / t @MikeVella)注入依赖项。
  • Martin Fowler记录了第三种替代方法(h / t @MarcDix),其中类显式实现了一个接口,用于它们希望注入的依赖性。

当不使用依赖注入时(例如在其构造函数中执行过多工作的类中),在单元测试中隔离组件往往变得更加困难。

早在2013年,当我写下这个答案时,这就是Google Testing Blog上的一个主要主题。这对我来说仍然是最大的优势,因为我们可能不一定总是在运行时设计中需要额外的灵活性(例如,用于服务定位器或者类似的模式),但是我们确实确实经常需要能够在测试期间隔离类。

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

例如,考虑以下情况:

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 :: addManagerPersonService :: removeManager的实现将需要GroupMembershipService的实例才能完成其工作。如果没有依赖注入,传统的方式是在" PersonService"的构造函数中实例化一个新的" GroupMembershipService",并在两个函数中都使用该实例属性。但是,如果GroupMembershipService的构造函数有很多要求,或者更糟糕的是,需要在GroupMembershipService上调用一些初始化" setter",那么代码会快速增长,并且现在取决于不仅在GroupMembershipService上,而且在GroupMembershipService所依赖的其他所有内容上。此外,与GroupMembershipService的链接被硬编码到PersonService中,这意味着我们不能"模仿" GroupMembershipService来进行测试,也不能在应用程序的不同部分使用策略模式。

使用依赖注入,我们无需将其实例化到PersonService中的GroupMembershipService,而是将其传递给PersonService构造函数,或者添加一个属性(getter和setter)以设置其本地实例。这意味着PersonService不再需要担心如何创建GroupMembershipService,而只需接受它所提供的内容并与之一起使用。这也意味着可以将任何属于GroupMembershipService的子类或者实现GroupMembershipService接口的东西"注入"到" PersonService"中,而" PersonService"不需要知道更改。

到目前为止,我发现的最佳定义是James Shore的定义:

"Dependency Injection" is a 25-dollar
  term for a 5-cent concept. [...]
  Dependency injection means giving an
  object its instance variables. [...].

马丁·福勒(Martin Fowler)的一篇文章可能也很有用。

依赖注入基本上是提供对象需要的对象(其依赖),而不是让对象自己构造它们。这是一种非常有用的测试技术,因为它允许对依赖项进行模拟或者存根。

可以通过多种方式将依赖项注入到对象中(例如构造函数注入或者setter注入)。甚至可以使用专门的依赖项注入框架(例如Spring)来做到这一点,但是肯定不是必需的。我们不需要那些框架具有依赖项注入。显式实例化和传递对象(依赖项)与通过框架进行注入一样好。