Dependency injection 通过构造函数或属性设置器进行依赖注入?

Dependency injection 通过构造函数或属性设置器进行依赖注入?,dependency-injection,inversion-of-control,Dependency Injection,Inversion Of Control,我正在重构一个类并向其添加一个新的依赖项。该类当前正在构造函数中获取其现有依赖项。为了保持一致性,我将参数添加到构造函数中。 当然,有几个子类,还有更多的子类用于单元测试,所以现在我正在玩改变所有构造函数以匹配的游戏,这需要时间。 这让我认为,将属性与setter一起使用是获得依赖关系的更好方法。我不认为注入的依赖项应该是构建类实例的接口的一部分。您添加了一个依赖项,现在您的所有用户(子类和直接实例化您的任何人)突然都知道了它。这感觉像是封装的中断 这似乎不是这里现有代码的模式,所以我希望找出普

我正在重构一个类并向其添加一个新的依赖项。该类当前正在构造函数中获取其现有依赖项。为了保持一致性,我将参数添加到构造函数中。
当然,有几个子类,还有更多的子类用于单元测试,所以现在我正在玩改变所有构造函数以匹配的游戏,这需要时间。
这让我认为,将属性与setter一起使用是获得依赖关系的更好方法。我不认为注入的依赖项应该是构建类实例的接口的一部分。您添加了一个依赖项,现在您的所有用户(子类和直接实例化您的任何人)突然都知道了它。这感觉像是封装的中断


这似乎不是这里现有代码的模式,所以我希望找出普遍的共识是什么,构造函数与属性的优缺点。使用属性设置器更好吗?

这主要是个人喜好的问题。 就个人而言,我倾向于选择setter注入,因为我相信它提供了更大的灵活性,可以在运行时替换实现。 此外,在我看来,具有大量参数的构造函数并不干净,并且构造函数中提供的参数应限于非可选参数

只要类接口(API)清楚执行其任务所需的内容,
很好。

我更喜欢构造函数注入,因为它有助于“强制”类的依赖性要求。如果它在c'tor中,消费者必须设置对象以使应用程序编译。如果您使用setter注入,则在运行时之前,他们可能不知道自己有问题,并且根据对象的不同,可能会在运行时出现问题


当注入的对象本身可能需要一系列工作(如初始化)时,我仍然时不时地使用setter注入。

如果您有大量可选依赖项(这已经是一种气味),那么setter注入可能是一种方法。构造函数注入可以更好地揭示您的依赖关系。

好吧,这取决于:-)

如果没有依赖项,类无法完成其工作,则将其添加到构造函数中。该类需要新的依赖项,因此您希望您所做的更改能够打破这些依赖项。此外,创建未完全初始化的类(“两步构造”)也是一种反模式(IMHO)


如果类可以在没有依赖项的情况下工作,那么setter就可以了。

当然,使用构造函数意味着您可以一次验证所有内容。如果将内容分配到只读字段中,则从构建时起就可以保证对象的依赖关系


添加新的依赖项是一件非常痛苦的事情,但至少这样编译器会一直抱怨,直到它正确为止。我认为这是一件好事。

我更喜欢构造函数注入,因为这似乎最符合逻辑。这就像说我的类需要这些依赖项来完成它的工作。如果它是可选的依赖项,那么属性似乎是合理的

我还使用属性注入来设置容器没有引用的内容,例如使用容器创建的演示者上的ASP.NET视图


我认为它不会破坏封装。内部工作应该保持内部,依赖关系处理不同的问题。

我个人更喜欢在构造函数中提取和重写“模式”而不是注入依赖关系,这主要是因为您问题中概述的原因。您可以将属性设置为
virtual
,然后在派生的可测试类中重写实现。

一般首选方法是尽可能多地使用构造函数注入

构造函数注入准确地说明了对象正常运行所需的依赖项-没有什么比更新对象并在调用方法时使其崩溃更令人恼火的了,因为没有设置某些依赖项。构造函数返回的对象应处于工作状态

尽量只使用一个构造函数,这样可以保持设计的简单性并避免歧义(如果不是针对人类,则针对DI容器)

当Mark Seemann在其著作《.NET中的依赖项注入》中称之为本地默认值时,您可以使用属性注入:依赖项是可选的,因为您可以提供良好的工作实现,但如果需要,希望允许调用方指定不同的实现

(先前的答复如下)



我认为如果注入是强制性的,那么构造函数注入会更好。如果添加了太多的构造函数,请考虑使用工厂而不是构造函数。

如果注入是可选的,或者如果您想中途更改,setter注入是很好的。我通常不喜欢setter,但这是一个品味问题。

类的用户应该知道给定类的依赖关系。例如,如果我有一个连接到数据库的类,并且没有提供注入持久层依赖项的方法,那么用户永远不会知道连接到数据库必须是可用的。但是,如果我改变构造函数,我会让用户知道持久层上存在依赖关系

此外,为了避免您必须更改旧构造函数的每次使用,只需将构造函数链接作为新旧构造函数之间的临时桥梁

public class ClassExample
{
    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo)
        : this (dependnecyOne, dependencyTwo, new DependnecyThreeConcreteImpl())
    { }

    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo, IDependencyThree dependencyThree)
    {
        // Set the properties here.
    }
}

依赖项注入的要点之一是揭示类具有哪些依赖项。如果类有太多依赖项,那么可能是时候进行一些重构了:类的每个方法都使用所有依赖项吗?如果没有,那么这是一个很好的起点,可以看到类可以在哪里被拆分。

一个值得考虑的选项是从简单的单个依赖项组成复杂的多个依赖项。也就是说,定义额外的