如果您 Thing
的系统中只需要一个 s 列表,您需要问自己这些东西是什么。它们是可以在每次运行之前配置的项目吗?它们会随着用户执行程序而改变吗?也许这些东西应该作为记录存储在数据库中,甚至是轻量级的内存数据库,如 Apache Derby 或 NoSQL 数据库。
即使您确实拥有一组固定的项目,您也应该考虑使用依赖注入系统并在单例范围内出售列表,而不是使用硬连线的单例。这样,您可以通过更改配置来替换测试类中的列表。
如果您发现最后一个令人困惑,请考虑这个。假设您有一个处理 的类Things
,例如在其中保留它们的静态列表。就像是:
public class ThingCatalog {
private static final List<Thing> things = new ArrayList<>();
public ThingCatalog() {
// initialize list of things.
}
public List<Thing> getThings() {
return things;
}
public Thing getThingWithId(int id) {
// ...
}
}
现在,您可以创建ThingCatalog
一个单例类;你已经看到了怎么做。将构造函数设为私有,并创建一个静态getInstance
方法。你会很想写
public class TreasureGenerator {
private ThingCatalog things = ThingCatalog.getInstance();
// ...
}
如果你想为这个类中不使用东西的方法编写单元测试会发生什么?你不需要这些东西,所以你根本不需要 ThingCatalog。不幸的是,你被它困住了。
你可以通过给 TreasureGenerator 一个 setThingCatalog 方法来解决这个问题:
public void setThingCatalog(ThingCatalog things) {
this.things = things;
}
当然,您只有一个 ThingCatalog,所以这并没有多大帮助。但是如果你有一个 ThingCatalog 可以实现的接口:
public interface ThingVendor {
List<Thing> getThings();
Thing getThingById(int id);
}
并且您所有的类都使用 ThingVendor 而不是 ThingCatalog,您可以在测试中替换它。
这是一个更像商业的例子。你正在编写一个财务程序,你需要让它在支票上打印今天的日期。典型的是编写如下代码:
String recipient = ...;
String accountOwner = ...;
BigDecimal amount = ...;
String accountNumber = ...;
Date today = new Date();
printCheck(...);
现在,有人问你“你的程序能正确处理闰日吗?” 十四年前,问题可能与千年虫有关。你将如何测试这个?你今天被这个方法困住了。
相反,您编写了一个名为的接口DateGenerator
:
public interface DateGenerator {
Date today();
}
public class TodayGenerator implements DateGenerator {
public Date today() { return new Date(); }
}
public class LeapDayGenerator implements DateGenerator {
public Date today() {
Calendar cal = Calendar.getInstance();
cal.set(2016, FEBRUARY, 29); // assume static imports;
return cal.getTime();
}
}
您的类将有一个 setDateGenerator 方法,并且您将正常使用 TodayGenerator。在您的闰日测试中,您将使用 LeapDayGenerator。
依赖注入系统使这些过程自动化。如果您继续从事计算,您将从经验中学到的是对象不应该知道如何配置自己。项目的其他部分应该将对象粘合在一起。