建造者模式验证 - 《Effective Java》

10
在《Effective Java(第2版)》的第2条中,作者提到了在使用构建器时对参数施加不变性的相关内容:
“非常重要的一点是,在从构建器复制参数到对象之后,必须检查它们,并且必须在对象字段而不是构建器字段上进行检查(第39条)。如果违反了任何不变式,则构建方法应该抛出IllegalStateException异常(第60条)。”
这是否意味着在构建方法创建目标对象后,应将其传递给验证例程以进行所需的验证?
此外,有人可以解释一下背后的原因吗?

1
我认为这是因为构建器字段在复制到对象后可能会发生变异(例如由另一个线程),这意味着构建器可能会处于创建“有效”对象的状态,而以前它将创建一个“无效”的对象。 - Andy Turner
抱歉,我不确定我是否正确理解了这个问题。一个构建器将被创建为 new <ClassName>.Builder.setter1().setter2.().build()。那么,任何其他线程如何获取对此特定构建器对象的引用? - Abhigyan Mehra
在这种情况下,它是不可能的。但一般情况下,没有什么可以阻止构建器实例在线程之间共享:它只是像任何其他引用一样。 - Andy Turner
2个回答

19

对象验证是使用构建器创建对象的重要组成部分。尽管可以有单独的例程执行验证,但这种分离并非必需:验证代码可以是执行构建操作的函数的一部分。换句话说,您可以这样做

TargetObject build() {
    TargetObject res = new TargetObject();
    res.setProperty1();
    res.setProperty2();
    validate(res); // This call may throw an exception
    return res;
}

void validate(TargetObject obj) {
    if (...) {
        throw new IllegalStateException();
    }
}

或这个:

TargetObject build() {
    TargetObject res = new TargetObject();
    res.setProperty1();
    res.setProperty2();
    if (...) {
        throw new IllegalStateException();
    }
    return res;
}

重要的是验证发生在目标对象构造之后而不是之前。换句话说,您需要验证对象的状态而不是构建器的状态。


1
另一个重要的事情是,你要验证的是对象,而不是构建器,因为你要对这个对象进行“有用的操作”,所以它必须是有效的。 - Andy Turner

16

构建器的build()方法通常会调用正在构建的类的私有构造函数。这就是为什么构建器通常作为静态嵌套类来实现,以便它们可以访问私有构造函数。构造函数是验证发生的地方。即使您没有使用构建器模式,构造函数也负责确保在创建对象时它处于有效状态。构造函数应该创建防御性副本(EJ item 39)并验证新对象的字段,而不是构建器的字段,因为在复制字段时构建器可能会发生变化。


这是一个比被接受的回答更好的答案。+1 - Manish Kumar Sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接