Java 将构造函数注入和默认构造函数一起使用是否不好?

Java 将构造函数注入和默认构造函数一起使用是否不好?,java,unit-testing,dependency-injection,Java,Unit Testing,Dependency Injection,我目前正在尝试改进用Java编写的遗留系统的可测试性。最常见的问题是存在无法模拟的“内部”依赖关系。解决这个问题的方法非常简单:引入依赖注入 不幸的是,代码库相当大,因此在整个应用程序中引入依赖注入将是一项巨大的工作,直到“引导”。对于我想要测试的每个类,我必须再更改100个(可能我在这里有点夸张,但肯定会很多)类,这取决于更改的组件 现在我的问题是:可以使用两个构造函数吗?一个默认构造函数用默认值初始化实例字段,另一个允许注入依赖项?使用这种方法有什么缺点吗?它允许将来使用依赖项注入,但仍然不

我目前正在尝试改进用Java编写的遗留系统的可测试性。最常见的问题是存在无法模拟的“内部”依赖关系。解决这个问题的方法非常简单:引入依赖注入

不幸的是,代码库相当大,因此在整个应用程序中引入依赖注入将是一项巨大的工作,直到“引导”。对于我想要测试的每个类,我必须再更改100个(可能我在这里有点夸张,但肯定会很多)类,这取决于更改的组件

现在我的问题是:可以使用两个构造函数吗?一个默认构造函数用默认值初始化实例字段,另一个允许注入依赖项?使用这种方法有什么缺点吗?它允许将来使用依赖项注入,但仍然不需要更改现有代码(尽管测试中的类)

当前代码(“内部”依赖项):

使用默认/di构造函数:

public class ClassUnderTest {

  private ICollaborator collab;

  public ClassUnderTest() {
    collab = new Collaborator();
  }

  public ClassUnderTest(ICollaborator collab) {
    this.collab = collab;
  }

  public void methodToTest() {
    //do something
    collab.doComplexWork();
    //do something
  }

}

这是绝对好的,我偶尔也会这样做,特别是对于服务风格的类,由于遗留代码或框架约束,需要使用默认构造函数

通过使用两个构造函数,可以清楚地分离默认的collaborator对象,同时仍然允许测试注入模拟。您可以通过保护第二个构造函数包,并将单元测试保持在同一个包中来强化这一意图


它不像成熟的依赖注入那样是一种好的模式,但这并不总是一种选择。

我认为这是完全可以的。我会根据参数化构造函数编写默认构造函数,但这可能是风格和偏好的问题


我更谨慎的是添加一个
setCollaborator()
-函数,因为这从根本上改变了ClassUnderTest的约定(和假设),如果从不了解历史记录、没有正确阅读文档的人编写的代码调用,这将导致奇怪的错误场景(或者可能根本没有任何文档…)

对我来说,这似乎是一个不错的策略。只要确保您可以使用实例字段,而不是在每次方法调用时重新实例化一个新实例。这两个代码段并不等效。如果必须在每次方法调用时创建一个新实例,则注入一个协作者工厂,而不是注入协作者。哦,您是对的当然。我在创建那个例子的时候没有考虑过。我会编辑这个例子。这叫做私生子注射,可能会有问题:谢谢你指出这一点和链接。不过,我认为我必须接受它,至少目前是这样。除此之外,最好记住,我的方法有助于脱钩增加了可测试性,但类在编译时仍然依赖于Collaborator。非常感谢您的回答!我还考虑过降低di构造函数的可见性。我不确定在这种情况下最好的方法是什么。公共构造函数将允许在将来的情况下更容易地重用应该使用依赖项(这是整个依赖项注入概念背后的想法,对吗?)。您认为公开它有什么缺点吗?
public class ClassUnderTest {

  private ICollaborator collab;

  public ClassUnderTest() {
    collab = new Collaborator();
  }

  public ClassUnderTest(ICollaborator collab) {
    this.collab = collab;
  }

  public void methodToTest() {
    //do something
    collab.doComplexWork();
    //do something
  }

}