有效 Java 中的 Builder 模式

我最近开始阅读 Joshua Bloch 的有效 Java。我发现 Builder 模式[书中的第2项]的想法非常有趣。我试图在我的项目中实现它,但是出现了编译错误。以下就是我试图做的事情:

具有多个属性的类及其生成器类:

public class NutritionalFacts {
private int sodium;
private int fat;
private int carbo;


public class Builder {
private int sodium;
private int fat;
private int carbo;


public Builder(int s) {
this.sodium = s;
}


public Builder fat(int f) {
this.fat = f;
return this;
}


public Builder carbo(int c) {
this.carbo = c;
return this;
}


public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}


private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}

尝试使用上面的类的类:

public class Main {
public static void main(String args[]) {
NutritionalFacts n =
new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
}
}

我得到以下编译器错误:

包含 有效的爪哇。建设者模式。营养事实。建设者 是必须的 新的营养知识 营养事实。建造者(10)。碳水化合物(23)。脂肪(1)。建造() ;

我不明白这信息是什么意思。请解释一下。上面的代码类似于 Bloch 在他的书中提出的例子。

130716 次浏览

这意味着你不能创建封闭类型。这意味着首先您必须确认一个“父”类的实例,然后从这个实例您可以创建嵌套的类实例。

NutritionalFacts n = new NutritionalFacts()


Builder b = new n.Builder(10).carbo(23).fat(1).build();

嵌套类

Builder 类应该是静态的。我现在没有时间实际测试更多的代码,但是如果它不能工作,请让我知道,我会再看一下。

您需要将 Builder内部类声明为 static

请参考有关 非静态内部类静态内部类的一些文档。

基本上,如果没有附加的外部类实例,非静态内部类实例就不可能存在。

使构建器成为 static类。那就成功了。如果它是非静态的,那么它将需要一个拥有它的类的实例——关键是不要有它的实例,甚至禁止在没有构建器的情况下创建实例。

public class NutritionFacts {
public static class Builder {
}
}

参考资料: 嵌套类

您正在尝试以静态方式访问一个非静态类。将 Builder更改为 static class Builder,它应该可以工作。

您给出的示例用法失败了,因为没有 Builder实例。所有实际用途的静态类始终是实例化的。如果你不让它静止,你需要说:

Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build();

因为每次都需要构造一个新的 Builder

要在 Intellij IDEA 生成一个内部构建器,请查看这个插件: https://github.com/analytically/innerbuilder

您应该将 Builder 类设置为静态的,还应该将字段设置为 final,并使用 getter 来获取这些值。不要为这些值提供 setter。通过这种方式,您的类将是完全不可变的。

public class NutritionalFacts {
private final int sodium;
private final int fat;
private final int carbo;


public int getSodium(){
return sodium;
}


public int getFat(){
return fat;
}


public int getCarbo(){
return carbo;
}


public static class Builder {
private int sodium;
private int fat;
private int carbo;


public Builder sodium(int s) {
this.sodium = s;
return this;
}


public Builder fat(int f) {
this.fat = f;
return this;
}


public Builder carbo(int c) {
this.carbo = c;
return this;
}


public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}


private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}

现在可以设置如下属性:

NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();

我个人更喜欢使用其他方法,当您有2个不同的类。所以不需要任何静态类。这基本上是为了避免在必须创建新实例时编写 Class.Builder

public class Person {
private String attr1;
private String attr2;
private String attr3;


// package access
Person(PersonBuilder builder) {
this.attr1 = builder.getAttr1();
// ...
}


// ...
// getters and setters
}


public class PersonBuilder (
private String attr1;
private String attr2;
private String attr3;


// constructor with required attribute
public PersonBuilder(String attr1) {
this.attr1 = attr1;
}


public PersonBuilder setAttr2(String attr2) {
this.attr2 = attr2;
return this;
}


public PersonBuilder setAttr3(String attr3) {
this.attr3 = attr3;
return this;
}


public Person build() {
return new Person(this);
}
// ....
}

所以,你可以这样使用你的构建器:

Person person = new PersonBuilder("attr1")
.setAttr2("attr2")
.build();

一旦你有了一个想法,在实践中,你可能会发现 Lombok 的 @Builder更方便。

@Builder允许您自动生成使您的类可实例化所需的代码,例如:

Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();

官方文件: https://www.projectlombok.org/features/Builder

正如许多已经在这里指出,你需要使类 static。 只是小的附加-如果你想,有一个有点不同的方式没有静态之一。

考虑一下。通过在类中声明类似于 withProperty(value)类型设置器的东西来实现构建器,并使它们返回对自身的引用。在这种方法中,您有一个单独的、优雅的类,它是一个线程安全且简洁的类。

想想这个:

public class DataObject {


private String first;
private String second;
private String third;


public String getFirst(){
return first;
}


public void setFirst(String first){
this.first = first;
}


...


public DataObject withFirst(String first){
this.first = first;
return this;
}


public DataObject withSecond(String second){
this.second = second;
return this;
}


public DataObject withThird(String third){
this.third = third;
return this;
}
}




DataObject dataObject = new DataObject()
.withFirst("first data")
.withSecond("second data")
.withThird("third data");

查看更多的 Java Builder例子。

你需要改变 建造者类到 静态类 Builder。然后它会工作得很好。

其他解决方案将实例化对象的内存分配增加一倍。

public class NutritionalFacts{


private int sodium;
private int fat;
private int carbo;


private NutritionalFacts(){}


public int getSodium(){ return sodium;}


public int getFat(){ return fat;}


public int getCarbo(){ return carbo;}


public static class Builder{
private NutritionalFacts nutrionalFacts;


public Builder(){
nutrionalFacts = new NutritionalFacts();
}


public Builder sodium(int s){
nutrionalFacts.sodium = s;
return this;
}


public Builder fat(int f){
nutrionalFacts.fat = f;
return this;
}


public Builder carbo(int c){
nutrionalFacts.carbo = c;
return this;
}


public NutritionalFacts build(){
return nutrionalFacts;
}
}
}