【问题标题】:Builder pattern validation - Effective Java构建器模式验证 - 有效的 Java
【发布时间】:2016-07-03 18:51:21
【问题描述】:

在 Effective Java(第 2 版)的第 2 项中,作者提到了以下关于在使用 Builders 时对参数施加不变量:

在将参数从构建器复制到对象后检查它们是至关重要的,并且它们是在对象字段而不是构建器字段上检查的(第 39 项)。如果违反了任何不变量,则构建方法应抛出 IllegalStateException(条款 60)。

这是否意味着在构建方法创建了目标对象之后,应该将其传递给验证例程以进行任何所需的验证?

另外,有人可以解释一下这背后的原因吗?

【问题讨论】:

  • 我想是因为构建器字段在被复制到对象后可能会被改变(例如,由另一个线程),这意味着构建器可以进入一个状态,它将创建一个“有效”对象,而以前它会创建一个“无效”对象。
  • 很抱歉,但我不确定我是否理解正确。构建器将被创建为新的 .Builder.setter1().setter2.().build()。任何其他线程如何获得此特定构建器对象的引用?
  • 在这种情况下,它不能。但是一般来说,没有什么可以阻止构建器实例在线程之间共享:它只是一个引用,就像任何其他引用一样。

标签: java design-patterns builder effective-java


【解决方案1】:

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

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;
}

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

【讨论】:

  • 另一件重要的事情是它是您验证的对象,而不是构建器,因为它是您要用来做“有用的事情”的对象,所以它必须是有效的.
【解决方案2】:

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

【讨论】:

  • 这是一个比公认的更好的答案。 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-27
  • 1970-01-01
  • 2015-08-24
  • 1970-01-01
  • 2012-03-23
  • 1970-01-01
相关资源
最近更新 更多