问题
在需要复杂验证逻辑的情况下,我如何才能最好地管理对象图的构造?出于可测试性的原因,我想保留依赖注入的、无操作的构造函数。
可测试性对我来说非常重要,您对维护代码的这个属性有何建议?
背景
我有一个普通的旧 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 {
...
}
}
可能的重复问题
- 验证逻辑应该在哪里实现?- 接受的答案对我来说是不可理解的。我将“在班级的本地环境中运行”读为重言式,验证规则如何在“班级的本地环境”之外的任何地方运行?第 2 点我无法理解,所以我无法发表评论。
- 在哪里提供验证的逻辑规则?- 两个答案都表明责任在于客户/数据提供者,我原则上喜欢这一点。但是,在我的情况下,客户端可能不是数据的发起者并且无法验证它。
- 在哪里保留验证逻辑?- 建议模型可以拥有验证,但是我发现这种方法不太适合测试。具体来说,对于每个单元测试,即使我正在测试模型的其他部分,我也需要关心所有的验证逻辑——按照建议的方法,我永远无法完全隔离我想要测试的内容。
到目前为止我的想法
尽管下面的构造函数公开声明了 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 的性能优势。
我的直觉是我应该停下来,征求外界的意见。我在正确的轨道上吗?