我读到单身人士对可测试性不利。你能详细说明吗?不能嘲笑单身人士吗?他们写作是因为紧耦合,但我看不出原因。
答案 0 :(得分:6)
这取决于你如何使用单例以及该单例是否实现了一个接口。如果它是单例的事实是实现细节,那么这可能没问题。如果你在要测试的代码中使用它的单例性质,那就更有问题了。
以我的Noda Time项目为例。它有一个IClock
接口,代表“获取当前时间的方式”。这有一个名为SystemClock
的实现,它是一个单例。现在考虑使用它的一些代码:
public class BadClass
{
public bool IsIt2016Yet()
{
var now = SystemClock.Instance.Now;
return now.InUtc().Year >= 2016;
}
}
我们无法在不改变SystemClock.Instance
的内容的情况下测试该代码。该代码与紧密耦合到单例类。但是,请考虑一下:
public class GoodClass
{
private readonly IClock clock;
public GoodClass(IClock clock)
{
this.clock = clock;
}
public bool IsIt2016Yet()
{
var now = clock.Now;
return now.InUtc().Year >= 2016;
}
}
现在你很好 - 在你的生产代码中设置了依赖注入,以便在SystemClock.Instance
的任何地方使用IClock
,但在测试代码中你可以使用假时钟 - 比如一个在NodaTime.Testing
包中提供。在生产中,代码最终将使用单例,但它并没有与它紧密耦合。
当然,假设您已经适当地设置了依赖注入。如果你想在任何地方构建实例而不关心它需要一个时钟的事实,你可以选择可选将类耦合到SystemClock
单例:
public GoodClass(IClock clock)
{
this.clock = clock;
}
public GoodClass() : this(SystemClock.Instance)
{
}
现在它仍然可以测试(并且通常是灵活的),因为可以传递一个时钟,但它很方便,因为它有效地“默认”到系统时钟。 (您可以使用可选参数执行此操作,并使用null
作为另一种选择,使SystemClock.Instance
的参数结束。)