没有内部 class 的构建器模式

Builder pattern without inner class

假设我有这个构建器模式。我到处搜索但找不到为什么我需要使用内部 class 如果外部 class 有 public consturctor.

public class User {

private final String firstName;
private final String surname;
private final int age;


public User(UserBuilder userBuilder) {
    this.firstName = userBuilder.firstName;
    this.surname = userBuilder.surname;
    this.age = userBuilder.age;
}



public static class UserBuilder {

    private final String firstName;
    private final String surname;
    private int age;

    public UserBuilder(String firstName, String surname) {
        this.firstName = firstName;
        this.surname = surname;
    }

    public UserBuilder age(int age) {
        this.age = age;
        return this;
    }


    public User build() {
        User user = new User(this);
        return user;
    }
}
}

在这里我可以重写这段代码而不使用内部 class as :

public class User {

private String firstName;
private String surname;
private int age;


public User(String firstName, String surname) {
 this.firstName = firstName;
 this.surname= surname;
}


public User age(int age) {
    this.age = age;
    return this;
}

}
}

当我读到构建器模式时,他们说这种模式可以防止大的构造函数块。他们使用 inner with setters(具有流畅的模式)。我不明白为什么我们需要创建内部 class 如果我的构造函数是 public,我可以在不使用内部 class 的情况下做同样的事情。 这是更复杂变量的另一个示例:

class NutritionFacts {
  private final int servingSize;
  private final int servings;
  private int calories;
  private int fat;
  private int sodium;
  private int carbohydrate;


    public NutritionFacts(int servingSize, int servings) {
        this.servingSize = servingSize;
        this.servings = servings;
    }
    public NutritionFacts calories(int val)
    { calories = val; return this; }
    public NutritionFacts fat(int val)
    { fat = val; return this; }
    public NutritionFacts sodium(int val)
    { sodium = val; return this; }
    public NutritionFacts carbohydrate(int val)
    { carbohydrate = val; return this; }
    
@Override
public String toString() {
    return "NutritionFacts{" +
            "servingSize=" + servingSize +
            ", servings=" + servings +
            ", calories=" + calories +
            ", fat=" + fat +
            ", sodium=" + sodium +
            ", carbohydrate=" + carbohydrate +
            '}';
}
}

构建器模式不是为了提供流畅的样式创建(尽管这是一个很好的好处),它是为了在存在多个 可选 参数时提供合理的对象实例化。

没有意义

public User someOptionalParam(String someOptionalParam) {
    this.someOptionalParam = someOptionalParam;
    return this;
}

由于您获取的User实例未完全构造,缺少非可选的名字、姓氏和年龄。

构建器模式在四人帮一书中得到普及(设计模式:可重用面向对象软件的元素)in a way that is not language specific。将构建器实现为内部 class 在 Java 语言的上下文中还有其他几个好处。

有效Java 给出一个构建器模式示例:


public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;
    
        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }
        public Builder calories(int val)
            { calories = val; return this; }
        public Builder fat(int val)
            { fat = val; return this; }
        public Builder sodium(int val)
            { sodium = val; return this; }
        public Builder carbohydrate(int val)
            { carbohydrate = val; return this; }
        
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }
    
    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

Java 缺少使模式有用的某些语言功能。 Kotlin 具有命名参数和可选参数,允许将上面的示例编写为:


class NutritionFacts(
    val servingSize : Int,
    val servings:     Int,
    val calories:     Int = 0;
    val fat           Int = 0,
    val sodium        Int = 0, 
    val carbohydrate  Int = 0
)

val aFact = NutritionFacts(servingSize = 10, servings = 2, fat = 7)

编辑: 感谢您完成练习并以您的风格重新实施 NutritionFacts

你的实现的问题是它是可变的,它可变的唯一原因是你实现 "building" 部分的方式。这个特定的 class 本身没有任何东西需要可变性。

NutritionFacts aFact = new NutritionFacts(1,2).calories(7).sodium(3);

// ....  

aFact.sodium(1).carbohydrate(9);

也许看起来我正在移动关于构建器模式通常应该实现的目标帖子,但我暗示 对此,当我说“作为内部 class 的构建器在 Java 语言的上下文中还有其他几个好处时。”