依赖注入:在无参数构造函数中实例化具体类时你会失去啥

Posted

技术标签:

【中文标题】依赖注入:在无参数构造函数中实例化具体类时你会失去啥【英文标题】:Dependency Injection: what do you lose when instantiating concrete classes in parameterless constructor依赖注入:在无参数构造函数中实例化具体类时你会失去什么 【发布时间】:2015-10-28 10:15:04 【问题描述】:

一般来说,我的依赖被抽象为接口,但在非测试使用时我只会使用一个具体的实现。比如我要写FooService

public class FooService : IFooService

    private readonly IBarDependency _barDependency;

    public FooService(IBarDependency barDependency)
    
        this._barDependency = barDependency;
    

    public FooService() : this (new BarDependency())
    
        // nothing to do here
    

    . . . // code goes here

现在纯粹主义者会惊恐地倒吸一口凉气,因为我在无参数构造函数中实例化了一个具体的类。过去,我对此不以为然,因为我知道我唯一不会使用具体类的时候是在进行单元测试和模拟依赖项时。

虽然它确实将FooService 类与BarDependency 类耦合,但它并不是紧密 耦合;这些类和接口也存在于同一个程序集中,所以在我看来,我并没有在这里失去很多东西,或者把自己画到一个角落里。我仍然可以轻松地测试FooService 类,而不会得到难以管理的代码,只需使用允许我传入模拟接口的构造函数。

所以问题是:这里实际上有什么风险?这种值得添加 IoC 容器的模式让我失去了什么?

【问题讨论】:

我在小项目中使用相同的方法。 DI 和其他“正确行事”规则通常会使小型项目过于复杂。当然,它带来了很多好处,但并不总是值得麻烦。 【参考方案1】:

例如,生命周期管理。也许您在整个代码中使用BarDependency 并且只希望一个实例处于活动状态。如果每个用户都创建自己的实例,则不能这样做。

如果您想用new NewBarDependency() 替换它,您还必须找到new BarDependency() 的所有用法。

容器为您解决了这两个问题,因为您可以在一种方法中配置对象的实例化和生命周期,即在哪里设置容器。

【讨论】:

所以您是在建议 IoC 容器更适合单例模式注入?这是有道理的。 是的,您可以使用“每个 AppDomain 一次”生命周期,因此您不需要单例或静态类。【参考方案2】:

视情况而定,但如果 FooService 需要被其他开发人员访问,则对 BarDependency 的依赖将基本隐藏。在我看来,不仅模拟和测试是使用依赖注入的原因,而且清楚地表明某些类使用了什么,以及它们需要什么才能正常工作。 也许我是个纯粹主义者,但是当这种模式是好的时,很容易走得更远,最终得到一个完全紧密耦合且难以维护的代码。

【讨论】:

【参考方案3】:

如果没有依赖注入,您将失去集中依赖管理各个方面的能力。当您将所有信息集中在一个地方而不是分散在代码中时,可视化依赖关系图和管理对象的生命周期会更容易。

一个具体的例子是,如果您想替换在某些(但不是所有)类中使用的IBarDependency 的默认实现。如果依赖项都连接在同一个文件或文件组中,那么推理要替换哪些依赖项会容易得多。

【讨论】:

以上是关于依赖注入:在无参数构造函数中实例化具体类时你会失去啥的主要内容,如果未能解决你的问题,请参考以下文章

Prism:在无参数构造函数中注入服务

如何使用手动依赖注入实例化一个类?

Unity构造函数注入代码示例

如何在 UWP 中实例化具有构造函数的页面?

Spring的反射机制和依赖注入

Java程序员必须掌握的Spring依赖管理原理