Item 2: Consider a builder when faced with many constructor parameters
阿新 • • 發佈:2020-12-15
場景1:當一個物件有多個引數需要初始化,分為必需引數和可選引數
方法一:
通過對建構函式進行過載來匹配不同情況,但此操作僅適用於引數較少的情況,在有很多引數的情況下,大大降低了程式碼可讀性和健壯性。
方法二:
使用JavaBean模式,該模式下,呼叫無參建構函式來建立物件,隨後通過setter方法來設定各引數。
顯而易見地,使用該方法也就意味著這個類不能是immutable class。
方法三:
使用builder模式,示例如下:
// Builder Pattern 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; } }
大致思路是在類中建立一個內部靜態類Builder,通過Builder中的setter對其屬性(可選引數)進行初始化(Builder中的建構函式通常用來初始化必需的引數),接著由Builder中的build函式呼叫外部類的建構函式並將Builder物件傳入,通過Builder物件中的初始化資訊來初始化外部類的屬性值,最後返回了初始化完畢的外部類物件。
即,外部類的建構函式通過獲取內部類Builder物件(裝載著初始化資料),拿出其中的資料初始化自己的屬性值。
呼叫如下:
NutritionFacts nutritionFacts = new NutritionFacts.Builder(240,8) .calories(100) .sodium(35) .carbohydrate(27) .build();
場景2:繼承出現在builder模式中
要注意不同層級的builder要返回對應的class,abstract類的builder返回值也是abstract的,實體類的builder也是實體的。同時要注意,為了限制返回型別為子類,通常採用遞迴泛型的方法。如下:
public abstract class Pizza { public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE } final Set<Topping> toppings; abstract static class Builder<T extends Builder<T>> { EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class); public T addTopping(Topping topping) { toppings.add(Objects.requireNonNull(topping)); return self(); } abstract Pizza build(); protected abstract T self(); } Pizza(Builder<?> builder) { toppings = builder.toppings.clone(); } }
注意到抽象類內部類Builder中的<T extends Builder
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override
public NyPizza build() {
return new NyPizza(this);
}
@Override
protected Builder self() {
return this;
}
}