是否应该避免服务对象的动态依赖?

时间:2011-08-17 10:02:40

标签: unit-testing language-agnostic dependency-injection

这个问题是关于主要基于价值对象和服务的可测试软件设计。

使用DI容器时,具有静态依赖关系的服务可以直接实例化或配置。但是,在某些情况下,服务需要仅在运行时已知的依赖项。

假设,想象一个简单的FileSystemDataStore,其中包含一些CRUD方法,用于管理目录中的文件。此服务需要一个目录名作为其构造函数参数之一。该名称只能在运行时知道,并且必须由其协作者提供。

这似乎有些问题,因为由于其动态特性,您无法在DI容器中配置此类服务。您可能必须使用工厂来创建此类服务。但是,这将导致服务客户的单元测试中的怪癖。您将不得不模拟工厂返回服务的模拟。这增加了单元测试的额外复杂性。返回嘲笑的嘲笑通常被认为是一种测试气味。

您对此问题有何看法?这是你的经历中的问题吗?这些服务是否应该被重构为更“纯粹”?

2 个答案:

答案 0 :(得分:6)

作为一般观察,当服务依赖于运行时值时,Abstract Factory is indeed the appropriate response

但是,正如问题中指出的那样, 会对测试的可维护性产生影响,因此如果您可以重新设计API以避免此类情况,则应该这样做。但这并不总是可能的。

答案 1 :(得分:1)

您想要注入目录名称,但在构建阶段不知道。我在这里看到三个选项。

<强> 1。注入提供者

而不是说&#34;这是您需要的目录名称&#34;你说的是#34;这是一个可以在运行时提供目录名称的对象&#34;。实现此目的的方法是声明构造函数参数Provider<String> directoryNameProvider。构造函数将对此提供程序的引用存储为成员变量。当在运行阶段调用apon来做一些实际工作时,当需要目录名时,类将包含这样的代码:

directoryName = directoryNameProvider.get();

在java中,您实现的界面是[javax.inject.Provider<T>][1]。这有一个方法:get()返回类型T。使用通用提供程序接口意味着您没有增加的接口。

当涉及到单元测试时,您可以注入一个匿名内部类,该类实现Provider<T>的单个方法,以便足够轻松地返回常量值。我们的代码库有一个SimpleProvider<T>类,它包含Provider接口中的给定对象。

Pro:允许您在主要构建阶段构造对象。单元测试非常简单。

Con:关于依赖关系创建问题的详细信息正在将泄漏到类中,因为它们应该完全是工厂的关注点。如果已经编写了类并且已经接受directoryName而不是directoryNameProvider,那就太糟糕了。

尽管看似很长的缺点列表,但这是我使用的一个选项。我认为这里缺少语言构造。

<强> 2。稍后构建麻烦的对象

您可以在了解更多内容时输入内部范围。在运行阶段方法中,您可以输入新范围。这意味着您将经历一个全新的迷你构建阶段,然后是一个小型运行阶段。这类似于您的应用程序main()中发生的事情,但处于较小的级别。

Pro:接收依赖的类仍然是纯粹的。

Con:进入和退出太多范围会使应用程序和对象生命周期难以理解。

第3。使用方法参数

您可以确定directoryName是一个方法参数,并在运行阶段将其传递给您的类,而不是尝试将其作为构造函数参数注入。这有效地决定了在这种情况下不使用依赖注入风格。

亲:简单

Con:将directoryName作为方法参数传递的类与需要它的类紧密耦合。实现依赖于数据库连接的替代实现将非常困难。

这些是我最近一直在考虑的问题,所以我对任何评论或编辑感兴趣。还有其他选择吗?