我对构建器模式非常感兴趣,我经常使用它,但是我不确定我制作的构建器是否足够好,并且我对可以使用它们的所有环境都表示怀疑。
这是我如何创建构建器的示例:
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的示例。你怎么看待这件事?
最佳答案
您的代码的任何部分都是不可变的。这也可能会妨碍线程安全。那说以二进制的方式声明一个类是线程安全还是不线程安全是非常困难的。我也看不出为什么首先要在线程之间共享构建器实例,但是我可能会因为代码示例的简单性而被误导。
为了使实现线程安全更加容易,您的Builder
本身应该是不可变的。这意味着每个withXXX()
方法都应返回一个代表新状态的新生成器。 (执行此操作的方法可能更聪明,但这将是直接的方法。)
只是重申一下:我不确定使构建器具有线程安全性是绝对必要的-大多数情况下,它们是具有非常短的生命周期和可见性范围的对象。是否要使它们不可变取决于使用情况,您可能想存储部分填充的构建器,但这也很少见。 (不过,从主观上来说,名称以with
开头的方法不就地修改对象的方法似乎更直观,而不是名称以set
开头的方法。)
通常这是无法回答的,但是如果您确实使Person
对象不可变,因此只能由您的构建器构造,则它们将无法用作JPA实体,我的猜测也是JSF支持bean。为您创建/管理某些对象的Java框架经常会希望它们不是JavaBean,这意味着可以通过反射调用无参数构造函数和属性 setter 来创建这些对象。