DI:要注射多少?

Kar*_*ten 18 dependency-injection

我正在编写我的第二个真实应用程序,它使用DI.总的来说,我认为它有一个更好的设计.但是有一些代码味道,我不知道如何解决.

我更喜欢使用构造函数注入,并经常观察到我需要在构造函数中注入大约5个或更多对象.它似乎太多了,也许这是一个设计问题,没有让SRP正确.但我认为我对DI的使用也应该受到指责.

我正在寻找"最佳实践"或"经验法则",总的来说,我似乎注入了一切,那些不在.Net框架中,是否过度了?

为了开始,以下是我注入的两个对象示例,但我不确定.

像应用程序配置或那些小型util类这样的真正单例的对象是否会注入它们?它们似乎经常注入,注入它们的唯一原因似乎是允许改变测试的价值,但Ayende似乎以另一种方式解决了这个问题:http://ayende.com/Blog/archive/2008 /07/07/Dealing-with-time-in-tests.aspx.

几乎每个对象都使用的常见对象,如日志记录,是否应该注入?

Ste*_*ven 15

我经常使用的经验法则是注入正确编写单元测试的东西.在执行此操作时,您有时会最终抽象出BCL类(例如DateTime.Now,File等),有时甚至是您自己的东西.注入的好东西是服务(例如ICustomerService,ICustomerUnitOfWorkFactory或ICustomerRepository).不要注入实体,DTO和消息之类的东西.

然而,注入对象还有其他原因,例如能够在以后更换模块(例如,用于验证,UI或O/RM的交换机实现),以允许在团队内部或跨团队进行并行开发,以及降低维护.

我更喜欢使用构造函数注入,并经常观察到我需要在构造函数中注入大约5个或更多对象.

正如您已经注意到的那样,由于没有遵守SRP,可能会导致许多依赖关系.但是,您可以做的是将具有逻辑的公共依赖项分组到聚合服务中并将其注入到使用者中.另请参阅Mark Seemann关于聚合服务的文章.

像应用程序配置或那些小型util类这样的真正单例的对象是否会注入它们?

我个人并不喜欢艾恩德提出的方式.这是一个Ambient Context,它是一种特定的服务定位器构造.这样做会隐藏依赖关系,因为类可以调用该静态类而无需注入它.明确地注入它会使您需要单独测试时间更加清晰.除此之外,它使得很难为MSTest这样的框架编写测试,这些框架倾向于并行运行测试.没有任何对策,它会使您的测试非常不可靠.-对于一个更好的解决方案DateTime.Now示例-是有一个IClock接口,建议在这里.正如您所看到的那样,该答案得分远高于Ayende方法,这在同一个SO问题中显示出来.

几乎每个对象都使用的常见对象,如日志记录,是否应该注入?

我在我的代码中注入它们,因为这使得依赖关系变得清晰.但请注意,在我的代码中,我几乎不必注入记录器.仔细考虑你要记录的每一行,这不是一个真正的失败(或者应该放在其他地方的交叉问题).当发生的事情我没想到时,我通常会抛出异常.它允许我快速找到错误.换句话说:不要过滤,但要快速失败.请问自己:" 我记得太多了吗? "

我希望这有帮助.


slu*_*ter 3

我个人的经验法则是这样的:

  • 如果你希望它不可变,则注入它
  • 如果您希望能够替换它用于测试目的,请注入它

服务之类的东西可以满足这两个标准——消费者永远不应该改变它,并且你希望能够在测试时替代它。对于不可变项,您仍然可能在消费对象上拥有一个属性,但该属性只有一个 getter,而不是 setter。如果您想更改该值,则必须创建该对象的新实例。

应该注入记录器吗?

没有理由这样做。记录器通常通过静态类公开,并且是从配置条目中更新的,因此即使出于测试目的也不需要注入它们。

是否应该注入真正的单例(例如应用程序配置)?

再次强调,它是一个全局可访问的对象,可以轻松修改以用于测试目的,因此无需注入。我唯一会注入这个的时候是消费者“断开连接”;即通过反射创建或作为 Web 服务或远程对象调用。

虽然 DI 是一种很好的模式,但太多的好东西仍然可能是不健康的。如果您感觉到代码气味越来越大,请检查您正在注入的每个项目并问自己一个问题:需要注入此参数吗?

  • 如果您将应用程序配置作为全局(或静态)访问,那么您的类依赖于整个该对象,并且很难在其他地方使用。最好注入对象关心的配置的特定部分。 (2认同)