使用依赖注入时,您注入哪些依赖项?
我之前已经注入了所有依赖项,但是在执行TDD时发现通常有两种类型的依赖项:
一种方法是注入所有依赖项,如
public ClassWithExternalDependency(IExternalDependency external,
IExtractedForTestabilityDependency internal)
{
// assign dependencies ...
}
但我发现这会导致DI注册表中的依赖性膨胀。
另一种方法是隐藏“可测性依赖性”,如此
public ClassWithExternalDependency(IExternalDependency external)
: this (external, new ConcreteClassOfInternalDependency())
{}
internal ClassWithExternalDependency(IExternalDependency external,
IExtractedForTestabilityDependency internal)
{
// assign dependencies ...
}
这是更多的努力,但似乎更有意义。缺点不是所有对象都在DI框架中配置,从而打破了我听过的“最佳实践”。
您会提倡哪种方法?为什么?
答案 0 :(得分:1)
我相信你最好注入所有依赖项。如果它开始变得有点笨拙,那可能表明您需要简化一些事情或将依赖项移动到另一个对象中。在你走的时候感受你设计的“痛苦”可能真的很有启发性。
对于注册表中的依赖性膨胀,您可以考虑使用某种传统的绑定技术,而不是手动注册每个依赖项。一些IoC容器内置了基于约定的类型扫描绑定。例如,这是我在使用Ninject的Caliburn WPF应用程序中使用的模块的一部分:
public class AppModule : NinjectModule
{
public override void Load()
{
Bind<IShellPresenter>().To<ShellPresenter>().InSingletonScope();
BindAllResults();
BindAllPresenters();
}
/// <summary>
/// Automatically bind all presenters that haven't already been manually bound
/// </summary>
public void BindAllPresenters()
{
Type[] types = Assembly.GetExecutingAssembly().GetTypes();
IEnumerable<Type> presenterImplementors =
from t in types
where !t.IsInterface
&& t.Name.EndsWith("Presenter")
select t;
presenterImplementors.Run(
implementationType =>
{
if (!Kernel.GetBindings(implementationType).Any())
Bind(implementationType).ToSelf();
});
}
即使我有几十个结果和演示者跑来跑去,我也不必明确注册。
答案 1 :(得分:0)
我当然不会注入所有依赖项,因为要停止?您想要注入string
依赖项吗?我只反转了单元测试所需的依赖项。我想存根我的数据库(例如,请参阅this example)。我想存根发送电子邮件。我想要存根系统时钟。我想将存根写入文件系统。
尽可能多地反转依赖项,即使是那些你不需要进行测试的依赖项,也就是说使单元测试变得更加困难,你发现的越多,你真正测试系统真正行为的方式就越少。这使您的测试更不可靠。它还会使应用程序根目录中的DI配置变得复杂。
答案 2 :(得分:0)
我会手动连接所有非外部依赖项,并“仅注册”外部依赖项。当我说非外部时,我的意思是属于我的组件的对象,并且为了单一责任/可测试性而将其提取到接口我永远不会有任何其他类似接口的实现。外部依赖项是数据库连接,Web服务,不属于我的组件的接口。我会将它们注册为接口,因为它们的实现可以切换到存根的实现以进行集成测试。在DI容器中注册少量组件使得DI代码更易于阅读和膨胀。