从 Java 基础到掌握 — 第 10 集
使用层和测试创建可维护的应用程序
该博客上撰写的文章是出于教学目的而创建的。 另外,文章中的信息可能不正确,请谨慎。
随着程序的发展,变化传播的程度变得比类的数量更重要。 让我们通过划分规则、存储和屏幕边界并使行为可测试来结束我们的 Java 系列。
责任层次
public final class OrderService {
private final ItemRepository repository;
public OrderService(ItemRepository repository) {
this.repository = repository;
}
public OrderResult buy(int itemId, int gold) {
Item item = repository.find(itemId)
.orElseThrow(() -> new ItemNotFoundException(itemId));
if (gold < item.getPrice()) {
return OrderResult.notEnoughGold();
}
return OrderResult.success(item, gold - item.getPrice());
}
}
该服务负责购买规则,并不直接了解数据库或HTTP实现细节。 控制器或屏幕将输入传递给服务并呈现结果。
将依赖关系置于外部
public interface ItemRepository {
Optional<Item> find(int id);
}
ItemRepository repository = new FileItemRepository(path);
OrderService service = new OrderService(repository);
如果在构造函数之外选择具体存储,测试可以用内存实现替换它。 接口并不是每个类的装饰,而是放置在外部技术变化的边界处。
试运行
@Test
void purchaseKeepsRemainingGold() {
ItemRepository repository = new MemoryItemRepository(
new Item(10, "Potion", 30)
);
OrderService service = new OrderService(repository);
OrderResult result = service.buy(10, 100);
assertEquals(70, result.getRemainingGold());
}
该测试检查外部可见的规则而不是内部方法调用顺序。 即使您清理了实现,您也可以查看相同的合同是否适用于相同的输入。
项目工具
Gradle 或 Maven 将编译、测试、库版本控制和打包成可重复的命令。 将构建文件和测试放在存储库中,以便项目不仅仅在开发环境中运行。
一个好的 Java 结构并不是有很多层次结构的。 它是一种结构,其中改变状态、外部资源和并发任务的生命周期的规则是明确的,并且可以在小单元中进行验证。