什么是依赖性注入?

已经有几个问题贴出了关于依赖注入的具体问题,比如什么时候使用它,有哪些框架可以使用它。然而。

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

到目前为止,我发现的最好的定义是James Shore的一个定义

"依赖注入"是一个25美元的 5分钱的概念的术语。[...] 依赖性注入意味着给一个 对象的实例变量。[...].

Martin Fowler的一篇文章也可能证明是有用的。

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

依赖关系可以通过许多方式注入到对象中(如构造函数注入或设置函数注入)。人们甚至可以使用专门的依赖注入框架(如Spring)来做到这一点,但它们当然不是必需的。你不需要这些框架来实现依赖注入。明确地实例化和传递对象(依赖关系)与框架注入一样,都是很好的注入方式。

评论(6)
解决办法

依赖注入是将依赖传递给其他对象框架(依赖注入器)。

依赖性注入使测试更容易。注入可以通过构造器完成。

SomeClass()的构造函数如下。

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

问题。 如果myObject涉及复杂的任务,如磁盘访问或网络访问,那么就很难SomeClass()进行单元测试。程序员必须模拟myObject,并可能拦截**的工厂调用。

另一种解决方案

  • myObject作为参数传递给构造函数
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject可以直接传递,这使得测试更加容易。

  • 一个常见的替代方法是定义一个无为的构造函数。依赖性注入可以通过设置器来完成。(h/t @MikeVella)。
  • Martin Fowler记录了第三种选择(h/t @MarcDix),即类明确地实现一个接口,用于程序员希望注入的依赖关系。

在没有依赖注入的情况下,要在单元测试中隔离组件是比较困难的。

在2013年,当我写下这个答案时,这是谷歌测试博客的一个主要主题。对我来说,这仍然是最大的优势,因为程序员在运行时设计中并不总是需要额外的灵活性(例如,对于服务定位器或类似的模式)。程序员经常需要在测试过程中对类进行隔离。

评论(10)

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

例如,考虑这些案例。

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的构造函数有多个要求,或者更糟糕的是,有一些初始化"setters"需要在GroupMembershipService上调用,代码会增长得相当快,而且PersonService现在不仅依赖于GroupMembershipService,而且还依赖于GroupMembershipService的其他一切。此外,与 "GroupMembershipService "的联系被硬编码到 "PersonService "中,这意味着你不能为了测试目的而虚构一个 "GroupMembershipService",或在你应用程序的不同部分使用一个策略模式。

通过依赖注入,你不需要在 "PersonService "中实例化 "GroupMembershipService",而是将其传递给 "PersonService "构造函数,或者添加一个属性(getter和setter)来设置它的本地实例。这意味着你的 "PersonService "不再需要担心如何创建 "GroupMembershipService",它只是接受它所给的那些,并与它们一起工作。 这也意味着任何属于 "GroupMembershipService "的子类或实现 "GroupMembershipService "接口的东西都可以被注入到 "PersonService "中,而 "PersonService "不需要知道这个变化。

评论(3)