协慌网

登录 贡献 社区

什么是依赖注入?

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

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

答案

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

对于 5 美分的概念,“依赖注入” 是一个 25 美元的术语。 [...] 依赖注入意味着为对象提供其实例变量。 [...]。

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

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

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

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

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

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 测试博客的一个主题。这对我来说仍然是最大的优势,因为您可能并不总是需要在运行时设计中具有额外的灵活性(例如,对于服务定位器或类似的模式),但是您通常需要能够在测试期间隔离您的类。

我发现松耦合这个有趣的例子:

任何应用程序都由许多对象组成,这些对象彼此协作以执行一些有用的东西。传统上,每个对象都负责获取它自己对与之协作的依赖对象(依赖项)的引用。这导致高度耦合的类和难以测试的代码。

例如,考虑一个Car对象。

Car取决于车轮,发动机,燃料,电池等运行。传统上,我们定义此类依赖对象的品牌以及Car对象的定义。

没有依赖注入(DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

这里, Car对象负责创建依赖对象。

如果我们想在最初的NepaliRubberWheel()穿孔后更改其依赖对象的类型(比如Wheel NepaliRubberWheel()会怎么样?我们需要重新创建具有新依赖关系的 Car 对象,例如ChineseRubberWheel() ,但只有Car制造商才能这样做。

那么Dependency Injection为我们做了什么......?

使用依赖项注入时,会在运行时向对象提供其依赖项,而不是编译时间(汽车制造时间) 。这样我们现在可以随时更换Wheel 。这里, dependencywheel )可以在运行时注入Car

使用依赖注入后:

在这里,我们在运行时注入 依赖项 (Wheel 和 Battery)。因此术语: 依赖注入。

class Car{
  private Wheel wh = [Inject an Instance of Wheel (dependency of car) at runtime]
  private Battery bt = [Inject an Instance of Battery (dependency of car) at runtime]
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

来源: 了解依赖注入