依赖注入样式之间的实际区别是什么?


12

我是依赖注入的新手,我对应在应用程序中使用哪种样式有一些疑问。我刚刚阅读了Martin Fowler 的《控制容器的反转》和《依赖注入》模式,但是我无法获得构造函数,setter和接口注入之间的实际区别。

在我看来,一个使用另一个的原因仅是代码清理和/或清晰的问题。有什么区别?相对于其他方法,使用它们有什么优点或缺点,还是我刚才所说的?

在我看来,构造函数注入是最直观的,而接口注入则是最少的。另一方面,setter注入是一个中间术语,但是,您是否应该能够更改最初注入的依赖对象的实例?这种注入方式是否可以确保需要依赖项的对象始终被注入?我相信不会,但是如果我错了,请纠正我。


不知道在此过程中还发现或阅读了哪些内容。我在这里这里发现了一些类似的话题。我是我的新手,可能会对您得到的答案感到好奇:)

@ user1766760您应该在这里帮助我,我的问题被投票为关闭,还有两票得到了解决!!投票解决问题或其他问题,我不知道该怎么做才能避免结案。
ecampver 2013年

erm ...我是SO的新手,所以我不太确定如何提供帮助/为何投票将其关闭(我看不到任何迹象?)。如果我大胆猜测,也许这不是一个特定的编程问题,而是更多的讨论?

您可能想扩大这个问题。依赖注入是“控制反转”的一种形式。还有其他选择。例如,服务位置是有用但成熟的替代方法。
2013年

Answers:


12

构造函数注入的优点是,它使依赖关系显式,并强制客户端提供实例。它还可以保证客户端以后不能更改实例。一个(可能的)缺点是您必须向构造函数添加一个参数。

Setter注入的优点是不需要向构造函数添加参数。它还不需要客户端设置实例。这对于可选的依赖项很有用。如果您希望类在默认情况下创建一个真实的数据存储库,那么这也可能很有用,例如,在测试中,您可以使用setter将其替换为测试实例。

据我所知,接口注入与二传手注入没有太大区别。在这两种情况下(可选),您都设置了一个以后可以更改的依赖项。

最终,这取决于偏好以及是否需要依赖关系。我个人几乎只使用构造函数注入。我喜欢通过强制客户端在构造函数中提供实例来使类的依赖关系显式化。我也喜欢客户端不能在事后更改实例。

通常,我传递两个单独的实现的唯一原因是进行测试。在生产中,我可以通过DataRepository,但在测试中,我可以通过FakeDataRepository。在这种情况下,我通常将提供两个构造函数:一个没有参数,另一个接受a IDataRepository。然后,在没有参数的构造函数中,我将调用链接到第二个构造函数并传递new DataRepository()

这是C#中的示例:


public class Foo
{
  private readonly IDataRepository dataRepository;

  public Foo() : this(new DataRepository())
  {
  }

  public Foo(IDataRespository dataRepository)
  {
    this.dataRepository = dataRepository;
  }
}

这就是所谓的穷人依赖注射。我喜欢它,因为在生产客户端代码中,我不需要通过重复几个看起来像这样的语句来重复自己

var foo = new Foo(new DataRepository());
但是,我仍然可以通过替代实现进行测试。我意识到,使用穷人的DI可以对依赖项进行硬编码,但这对我来说是可以接受的,因为我主要使用DI进行测试。


谢谢,这与我的理解非常接近,但是仍然有什么情况不能使用构造函数注入吗?
ecampver,2013年

1
您可能不想对可选依赖项使用构造函数注入。我认为没有其他原因不使用它。
jhewlett

我专门使用属性设置器注入来填充一些包含大量值的配置类。我不会在其他任何地方使用它。我总是对可选参数的理由有点怀疑,因为很有可能这违反了单一责任规则。当然,规则注定要被打破,所以……
Ian

@jhewlett-太好了,我一直在寻找一种很好的简单的单元测试存根技术,它不会使我的解决方案复杂化-我认为就是这样:)
Rocklan

2

上面已经充分描述了构造函数和setter注入之间的差异,因此我将不对其进行详细说明。

接口注入是一种更高级的注入形式,它很有用,因为它允许在使用依赖项时而不是在将使用它的对象初始化期间确定依赖项。这提供了许多有用的选项:

  • 依赖项的范围可以不同于注入它的对象;例如,您可以使用接口注入来提供一个对象,该对象在每个用户会话或每个线程中都存在一个全局单例。每当对象需要依赖项时,它将调用框架提供的getter方法,并且根据调用对象的情况,该方法可以返回不同的结果。

  • 它允许延迟初始化-无需依赖项就可以初始化,直到将要使用它为止

  • 它允许依赖项从存在时的缓存副本中加载,或者在不存在时重新初始化(例如,SoftReference在Java中使用a )。

显然,像这样的先进技术有缺点。在这种情况下,主要问题是代码变得不太清晰(代码中使用的类变为抽象,并且没有明显的具体实现,如果您不习惯,可能会造成混淆),并且您变得更加依赖在依赖注入框架上(当然,仍然可以手动实例化对象,但是比其他注入样式更难)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.