【问题标题】:Does Dependency Injection (DI) rely on Interfaces?依赖注入 (DI) 是否依赖于接口?
【发布时间】:2010-07-15 15:15:18
【问题描述】:

这对大多数人来说似乎很明显,但我只是想确认依赖注入 (DI) 依赖于接口的使用。

更具体地说,对于在其构造函数中具有某个接口作为参数的类或将某个接口定义为属性(又名 Setter)的情况下,DI 框架可以将具体类的实例移交给满足该类中该接口的需求。 (如果此描述不清楚,请致歉。我无法正确描述此内容,因为术语/概念对我来说仍然有些新。)

我问的原因是我目前有一个具有某种依赖关系的类。与其说是对象依赖,不如说是 URL。该类看起来像这样 [C#]:

using System.Web.Services.Protocols;
public partial class SomeLibraryService : SoapHttpClientProtocol 
{
        public SomeLibraryService() 
        {
            this.Url = "http://MyDomainName.com:8080/library-service/jse";
        }
}

SoapHttpClientProtocol 类有一个名为Url 的公共属性(这是一个普通的旧“字符串”),这里的构造函数将它初始化为一个硬编码的值。

我可以使用 DI 框架在构建时注入不同的价值吗?我不认为this.Url 不是Interface;这是String

[顺便说一下,根据我正在使用的代码中的 cmets,上面的代码是“由 wsdl 自动生成的”。所以我并不特别想更改这段代码,尽管我也没有看到自己重新生成它。所以也许改变这个代码是好的。]

我可以看到自己制作了一个替代构造函数,它接受一个字符串作为参数并以这种方式初始化this.Url,但我不确定这是保持松散耦合关注点分离的正确方法。 (SoC)

对这种情况有什么建议吗?

【问题讨论】:

    标签: c# dependency-injection separation-of-concerns


    【解决方案1】:

    DI 实际上只是意味着一个类不会构造它的外部依赖项,并且不会管理这些依赖项的生命周期。可以通过构造函数或方法参数注入依赖项。接口或抽象类型通常用于阐明消费者对其依赖项的期望,但在某些情况下也可以注入简单类型。

    例如,库中的类可能会在内部调用 HttpContext.Current,这会对托管代码的应用程序做出任意假设。库方法的 DI 版本会期望通过参数注入 HttpContext 实例,等等

    【讨论】:

      【解决方案2】:

      不需要使用接口——您可以使用具体类型或抽象基类。但 DI 的许多优点(例如能够更改依赖项的实现)都来自使用接口。

      Castle Windsor(我最了解的 DI 框架)允许您将 IoC 容器中的对象映射到接口,或仅映射到名称,这适用于您的情况。

      【讨论】:

      • 我给你打了绿色复选标记,但大卫和彼得的答案也非常好。感谢您澄清不需要接口。
      • "...come when using interfaces" 这是不正确或具有误导性的。我认为要么删除它,要么使用抽象基类来支持它如何阻止我更改依赖项的实现。
      • @LukePuplett:如果您使用模拟框架来构建用于单元测试的对象(DI 的核心理由之一),您将需要使用接口。
      • @JamesCurran 不,就像手动为多态性子类化,模拟在相同的约束下工作;成员必须是抽象的或虚拟的。正如 David 所说,DI 是向要使用的类或系统提供依赖项的行为,即使是手动的,也不需要像 Unity 这样的自动化 DI 容器。关于你的问题,我不使用模拟库。我编写了非常好的赝品,我只测试公共 API 的行为,让我可以经常轻松地进行重构。模拟模糊了测试并将测试与实现结合起来。必须模拟自己的代码是设计缺陷的标志。
      【解决方案3】:

      依赖注入是一种组织代码的方式。也许您的一些困惑来自这样一个事实,即没有一种官方的方法可以做到这一点。它可以使用“常规”c# 代码或使用 Castle Windsor 之类的框架来实现。有时(经常?)这涉及使用接口。不管如何实现,DI 的总体目标通常是让您的代码更易于测试和以后更易于修改。

      如果您要通过构造函数在示例中注入 URL,则可以将其视为“手动”DI。 DI 上的 Wikipedia article 有更多手动与框架 DI 的示例。

      【讨论】:

      • 是的,我同意。一些混乱来自没有标准化的方法。 DI 已通过“Pro ASP.net MVC Framework”一书(Sanderson 撰写)呈现给我,并且显示的每个示例都使用接口,所以我的结论是它只适用于接口,但从逻辑上讲,它在我的脑海中没有意义,所以我不得不问。 Sanderson 在他的第一本书中谈到了 Castle Windsor,但现在在他的第二版中使用了 Ninject(上周刚从印刷机上印出来)。是的,我开始意识到 DI 对单元测试有很大帮助。
      【解决方案4】:

      我想重点回答在 .NET 应用程序中使用接口。 .NET 中的多态性可以通过虚拟或抽象方法或接口来实现。

      在所有情况下,都存在根本没有实现的方法签名或可以覆盖的实现。

      定义了函数(甚至是属性)的“契约”,但是方法是如何实现的,方法的逻辑内容在运行时可能会有所不同,这取决于实例化哪个子类并将其传递给方法或构造函数,或在属性上设置(“注入”行为)。

      官方的 .NET 类型设计指南提倡在接口上使用抽象基类,因为它们在发布后有更好的选择来发展它们,可以包括便利的重载,并且能够更好地自我记录并向实现者传达正确的用法。

      但是,必须注意不要添加任何逻辑。这样做的诱惑在过去让人们感到很痛苦,所以很多人使用接口 - 许多其他人使用接口仅仅是因为坐在他们周围的程序员就是这样做的。

      有趣的是,虽然 DI 本身很少被过度使用,但使用框架来执行注入却经常被过度使用而损害了复杂性的增加,在越来越多的地方可能会发生连锁反应容器中需要类型,即使它们从未“切换”过。

      应谨慎使用 IoC 框架,通常仅当您需要根据环境或配置在运行时换出对象时。这通常意味着切换应用程序中的主要组件“接缝”,例如用于抽象数据层的存储库对象。

      对我来说,IoC 框架的真正威力是在您无法控制创建的地方切换实现。例如,在 ASP.NET MVC 中,控制器类的创建是由 ASP.NET 框架执行的,因此注入任何东西都是不可能的。 ASP.NET 框架有一些钩子,IoC 框架可以使用这些钩子来“介入”创建过程并发挥它们的魔力。

      卢克

      【讨论】:

      猜你喜欢
      • 2020-04-14
      • 2023-03-23
      • 2017-06-10
      • 2021-11-19
      • 2012-11-28
      • 1970-01-01
      • 2021-04-24
      • 1970-01-01
      相关资源
      最近更新 更多