通过构造函数或属性设置程序进行dependency injection?

我正在重构一个类,并添加一个新的依赖关系。 该类正在构造函数中使用现有的依赖关系。 所以为了一致性,我将这个参数添加到构造函数中。
当然,还有几个子类加上unit testing,所以现在我正在玩游戏,改变所有的构造函数来匹配,并且需要很长时间。
这让我觉得使用setter属性是获取依赖关系的更好方法。 我不认为注入的依赖关系应该是构造一个类的实例的接口的一部分。 你添加一个依赖关系,现在所有的用户(子类和任何直接实例化你的用户)突然知道它。 这感觉就像封装的rest。

这似乎不是现有代码的模式,所以我正在寻找一般共识是什么,build设者与财产的利弊。 是更好地使用属性设置?

那么,这取决于:-)。

如果这个类没有依赖关系就无法完成它的工作,那么把它添加到构造函数中。 这个类需要新的依赖关系,所以你希望你的改变破坏事物。 另外,创build一个没有完全初始化的类(“两步构造”)是一个反模式(IMHO)。

如果这个类没有依赖性,那么一个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. } } 

dependency injection的一个要点是揭示这个类有什么依赖关系。 如果类有太多的依赖关系,那么可能是重构的时候了:类的每个方法是否都使用了所有的依赖关系? 如果没有,那么这是一个很好的起点,看看class级可以分开的地方。

当然,使用构造函数意味着你可以一次validation所有的东西。 如果您将内容分配到只读字段中,则您可以从构build时间起就确保对象的相关性。

这是一个真正的痛苦增加新的依赖,但至less这样编译器不断抱怨,直到它是正确的。 我认为这是一件好事。

如果你有大量可选的依赖关系(这已经是一种气味),那么可能需要setter注入才行。 构造函数注入更好地揭示你的依赖关系。

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

构造函数注入正好说明了什么是对象正常工作所需的依赖关系 – 没有什么比创build一个对象更麻烦了,并且在调用一个方法时崩溃了,因为一些依赖没有被设置。 由构造函数返回的对象应处于工作状态。

尝试只有一个构造函数,它保持简单的devise,避免歧义(如果不是人类,DI容器)。

您可以使用属性注入,当您有什么马克Seemann在他的书“dependency injection在.NET”中调用本地默认值 :依赖项是可选的,因为您可以提供一个良好的工作实现,但希望允许调用方指定一个不同的需要。

(以下答案)


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

如果注射是可选的,或者如果你想在中途改变它,注射器是很好的。 我通常不喜欢二传手,但这是一个品味问题。

我个人比较喜欢在构造函数中注入依赖的Extract和Override “模式”,主要是因为你的问题中提到的原因。 您可以将属性设置为virtual ,然后覆盖派生的可testing类中的实现。

这主要是个人品味的问题。 我个人倾向于更喜欢setter注入,因为我相信它给你更多的灵活性,可以在运行时replace实现。 另外,在我看来,有很多参数的构造函数并不干净,构造函数中提供的参数应该限制为非可选参数。

只要类接口(API)在执行任务时需要清楚,那么就很好。

我更喜欢构造器注入,因为这看起来是最合乎逻辑的。 就像我的class级要求这些依赖来完成它的工作一样。 如果它的可选依赖,那么属性看起来是合理的。

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

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

我更喜欢构造函数注入,因为它有助于“强制”类的依赖性需求。 如果在c'tor中,消费者必须设置对象才能让应用程序编译。 如果您使用setter注入,他们可能不知道他们有问题,直到运行时间 – 并根据对象,它可能是在运行时间迟了。

我仍然使用setter注入时,注入的对象可能需要一些工作本身,如初始化。

可能值得考虑的一个选项是用简单的单一依赖关系组合复杂的多重依赖关系。 也就是说,为复合依赖定义额外的类。 这使得事情变得简单了WRT构造器注入 – 每个调用的参数更less – 同时仍然保持必须提供的所有依赖关系来实例化事物。

当然,如果存在某种逻辑上的依赖关系组合,那么这个组合是非常有意义的,所以这个化合物不仅仅是一个任意的聚合,而且对于一个化合物依赖关系是否有多个依赖关系是最有意义的 – 但是参数模块“pattern”已经有很长一段时间了,而且我见过的大部分都是非常随意的。

但个人而言,我更喜欢使用方法/属性设置器来指定依赖项,选项等等。调用名称帮助描述正在发生的事情。 不过,提供示例即如何设置它的代码片段是一个好主意,并且确保依赖类进行了足够的错误检查。 您可能需要使用有限状态模型进行设置。

我最近遇到了一个情况 ,我在一个类中有多个依赖关系,但是每个实现中只有一个依赖关系会发生变化。 由于数据访问和错误日​​志依赖关系可能只是出于testing目的而改变,我为这些依赖关系添加了可选参数 ,并在构造函数代码中提供了这些依赖关系的默认实现。 通过这种方式,该类保持其默认行为,除非该类的消费者被覆盖。

使用可选参数只能在支持它们的框架中完成,比如.NET 4(对于C#和VB.NET,尽pipeV​​B.NET一直有它们)。 当然,你可以简单地使用一个可以被你的类的消费者重新分配的属性来完成类似的function,但是你没有获得通过将私有接口对象分配给构造函数的参数而提供的不变性的优点。

所有这一切被说,如果你正在引入一个新的依赖,必须由每个消费者提供,你将不得不重构你的类的构造函数和所有的代码。 我上面的build议确实只适用于如果你有能力提供所有当前代码的默认实现的豪华,但仍然能够覆盖默认实现,如果有必要。

这取决于你想如何实现。 我更喜欢构造器注入,无论我感觉实现的价值是否经常改变。 例如:如果compnay stragtegy与oracle服务器一起使用,我将通过构造函数注入来configuration我的数据源值,以实现bean的连接。 否则,如果我的应用程序是一个产品,并有机会可以连接到客户的任何数据库,我会通过setter注入来实现这样的数据库configuration和多品牌实施。 我刚刚举了一个例子,但是有更好的方法来实现上面提到的场景。

构造函数注入确实显示了依赖关系,如果在构造函数中检查参数,使得代码更具可读性并且不容易出现未处理的运行时错误,但这确实归结为个人观点,而且越多使用DI,往往会根据项目来回摇摆。 我个人遇到类似构造函数的代码气味问题,这些代码具有很长的参数列表,而且我觉得对象的使用者应该知道依赖关系以便使用对象,所以这使得使用属性注入成为可能。 我不喜欢财产注入的隐含性质,但我觉得它更优雅,导致更清晰的代码。 但另一方面,构造函数注入提供了更高程度的封装,根据我的经验,我尝试避免使用默认的构造函数,因为如果不小心的话,它们可能会对封装数据的完整性产生不良影响。

根据您的具体情况,通过构造函数或属性明智地select注入。 而且不要因为看起来必须使用DI,而是会阻止糟糕的devise和代码味道。 如果努力和复杂性超过收益,有时候使用模式是不值得的。 把事情简单化。

这是一个旧的post,但如果将来需要它,这可能是有用的:

https://github.com/omegamit6zeichen/prinject

我有一个类似的想法,并提出了这个框架。 这可能还远远没有完成,但它是一个注重财产注入的框架