问题
在需要进行复杂验证逻辑的情况下,最好如何管理对象图的构建?我希望保留依赖注入和无操作构造函数以实现可测试性。
可测试性对我来说非常重要,您的建议如何维护代码的这一属性?
背景
我有一个普通的 Java 对象,它为我管理一些业务数据的结构:
class Pojo
{
protected final String p;
public Pojo(String p) {
this.p = p;
}
}
我希望确保p的格式是有效的,因为如果没有这个保证,这个业务对象就没有意义;如果p是无意义的话,甚至不应该创建它。然而,验证p是非常棘手的。 注意事项 实际上,这需要复杂的验证逻辑,以至于这个逻辑应该可以完全在自己的类中进行测试,所以我把这个逻辑放在了一个单独的类中:
final class BusinessLogic() implements Validator<String>
{
public String validate(String p) throws InvalidFoo, InvalidBar {
...
}
}
可能存在的重复问题
- 应该在哪里实现验证逻辑? - 接受的答案对我来说难以理解。我将“在类的本地环境中运行”解读为同义反复,那么验证规则除了在“类的本地环境”中运行,还能在什么地方运行呢?第二点我无法理解,因此无法评论。
- 在哪里提供验证逻辑规则? - 两个答案都建议客户端/数据提供者负责,这在原则上是可以接受的。然而,在我的情况下,客户端可能不是数据的发起者,无法对其进行验证。
- 在哪里保留验证逻辑? - 建议模型拥有验证功能,但我认为这种方法不太理想用于测试。具体而言,对于每个单元测试,我需要关心所有验证逻辑,即使我正在测试模型的其他部分,我也无法完全隔离我想要测试的内容,遵循建议的方法。
我的思考
尽管以下构造函数公开声明了Pojo的依赖关系并保留其简单的可测试性,但它完全不安全。这里没有任何东西可以阻止客户端提供声称每个输入都是可接受的验证器!
public Pojo(Validator businessLogic, String p) throws InvalidFoo, InvalidBar {
this.p = businessLogic.validate(p);
}
因此,我对构造函数的可见性进行了一定的限制,并提供了一个工厂方法来确保验证后再进行构造:
@VisibleForTesting
Pojo(String p) {
this.p = p;
}
public static Pojo createPojo(String p) throws InvalidFoo, InvalidBar {
Validator businessLogic = new BusinessLogic();
businessLogic.validate(p);
return new Pojo(p);
}
现在我可以将createPojo重构为工厂类,这将恢复"单一职责"到Pojo,并简化工厂逻辑的测试,更不用说不再浪费地在每个新Pojo上创建一个新(无状态)BusinessLogic带来的性能优势了。
我的直觉告诉我应该停下来,寻求外部意见。我是否正确?