我对构建器模式非常感兴趣,我经常使用它,但我不确定我制作的构建器是否足够好,而且我对可以使用它们的所有环境也有疑问。
这是我如何创建构建器的示例:
public class Person {
private String name;
private String secondName;
private int age;
public static class Builder {
private boolean isBuilt;
private Person person = new Person();
private void check() {
if (isBuilt) {
throw new IllegalStateException(
"The object cannot be modified after built");
}
}
public Builder withName(String name) {
check();
person.name = name;
return this;
}
public Builder withSecondName(String secondName) {
check();
person.secondName = secondName;
return this;
}
public Builder withAge(int age) {
check();
person.age = age;
return this;
}
public Person build() {
check();
isBuilt = true;
return person;
}
}
@Override
public String toString() {
return "Name: " + name + "\nSecond name:" + secondName + "\nAge:" + age;
}
}
只是一个快速使用示例:
Person person = new Person.Builder()
.withName("John")
.withSecondName("Smith")
.withAge(50)
.build();
System.out.println(person);
以下是我的一些疑惑:
我在互联网上看到一些例子,说类级别的变量必须是最终的并通过构造函数传递。我还看到了一个例子,其中变量被声明为 volatile。你怎么看待这件事?
最佳答案
Do you think it is really immutable? [...] Is this really thread safe?
您的代码的任何部分都不是一成不变的。这也可能会妨碍线程安全;也就是说,很难以二进制方式声明一个类是否是线程安全的。我也不明白为什么你会首先在线程之间共享构建器实例,但我可能会被你的代码示例的简单性所误导。
为了更轻松地实现线程安全,您的
Builder
s 本身应该是不可变的。这意味着每个 withXXX()
方法应该返回一个代表新状态的新构建器。 (可能有更聪明的方法可以做到这一点,但这将是最直接的方法。)只是重申一下:我不确定是否有必要使构建器线程安全 - 大多数情况下它们是生命周期和可见范围非常短的对象。您是否想让它们不可变取决于用例,您可能想要存储部分填充的构建器,但这也很少见。 (但主观上,名称以
with
开头的方法似乎更直观,而不是就地修改对象,而不是名称以 set
开头的方法。)Do you think that this builder would have any limitation in what regards the scenario where it can be used?
这通常是无法回答的,但如果你确实让你的
Person
对象不可变,因此只能由您的构建器构建,它们将无法用作 JPA 实体,我的猜测也是作为 JSF 支持 bean。为您创建/管理某些对象的 Java 框架通常不希望它们是 JavaBean,这意味着可以通过反射调用无参数构造函数和属性 setter 来创建这些对象。
关于java - 在正确的地方使用正确的构建器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14803844/