我正在开发一个拥有大量现有代码库的项目,我的任务是对其中的一小部分进行一些重新设计。现有的代码库没有大量的单元测试,但我希望至少我正在努力的部分有一套完整的单元测试和良好的代码覆盖率。
因为现有的代码库并没有考虑单元测试,所以我需要进行一些结构更改以支持依赖的隔离。我试图尽可能减少对现有代码的影响,但我确实有一些余地来改变其他模块。我想知道我所做的改变是否有意义,或者是否有更好的方法来解决问题。我做了以下更改:
我为我的'coordinator'类所依赖的所有非平凡类提取了接口。其他开发人员勉强允许这一点,但有一些轻微的抱怨。
我修改了协调器类,只引用其操作的接口。
我无法使用DI框架,但我必须注入大量依赖项(包括作为类操作的一部分的附加依赖项实例化),因此我将协调器类修改为这样(域 - 具体名称简化):
public sealed class Coordinator
{
private IFactory _factory;
public Coordinator(int param)
: this(param, new StandardFactory())
{
}
public Coordinator(int param, IFactory factory)
{
_factory = factory;
//Factory used here and elsewhere...
}
public interface IFactory
{
IClassA BuildClassA();
IClassB BuildClassB(string param);
IClassC BuildClassC();
}
private sealed class StandardFactory : IFactory
{
public IClassA BuildClassA()
{
return new ClassA();
}
public IClassB BuildClassB(string param)
{
return new ClassB(param);
}
public IClassC BuildClassC()
{
return new ClassC();
}
}
}
这允许注入存根工厂以返回单元测试的模拟依赖项。我担心的是,这也意味着这个班级的任何用户都可以提供自己的工厂来改变班级的运作方式,这不是真正的意图。此构造函数纯粹用于单元测试,但它具有暗示需要额外可扩展性的副作用。我通常不会在我使用的其他类中看到这种模式,这让我想知道我是否过度复杂,以及是否有更好的方法来实现所需的隔离。
注意:代码格式化有些奇怪,所以我为上面的不良格式道歉。不知道为什么它不会让我正确格式化。
答案 0 :(得分:3)
建议的设计是 Constructor Injeciton 的变体,有时称为 Bastard Injection 。在绿地情况下,我会认为Bastard Injection是一种反模式,但它在像你描述的那种棕色地带情况下是一个很好的妥协。
你不应该真正关心Coordinator类隐含的可扩展性。正确完成,testability is extensibility,理想情况下,注入的IFactory应该代表一个适当的域概念,以实现可变性。我理解当你从遗留代码重构时,这可能很难/不可能实现,但这是你的代码库应该移动的方向。
在任何情况下,如果您的同事已经吝啬接口的提取,他们可能不太可能将IFactory构造函数参数解释为可扩展点。
祝你好运。答案 1 :(得分:2)
您可能需要考虑使用接受IFactory
内部的构造函数 - 这会阻止程序集外的其他用户“扩展”您的Coodinator。然后使用InternalsVisibleTo
属性将其公开给您的单元测试。