《Effective Java》第 2 條「遇到多個構造器參數時要考慮用構建器」中提到:
「因為構造過程被分到了幾個調用之中,在構造過程中 JavaBean 可能處於不一致的狀態。」
指對象已部分初始化,但尚未滿足類的所有不變約束。例如:
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 NutritionFacts(int servingSize, int servings) { ... }
public NutritionFacts(int servingSize, int servings, int calories) { ... }
public NutritionFacts(int servingSize, int servings, int calories, int fat) { ... }
// ...
}
調用者可能寫錯參數順序:`new NutritionFacts(100, 200, 0, 10, 20)` → `calories=0, fat=10, sodium=20, carbohydrate=0`(預設),語義錯誤且編譯不報錯。
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;
private NutritionFacts(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
}
public static class Builder {
// 必填參數
private final int servingSize;
private final int servings;
// 選填參數 - 預設值
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);
}
}
}
// 使用:語義清晰、順序無關、必填強制
NutritionFacts facts = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
書中提到的「幾個調用的先後順序不同」在多執行緒場景尤為明顯:
| 模式 | 缺陷 |
|---|---|
| 無參構造 + Setter | 對象可變、構造中間態對外可見、難以保證不變約束、非執行緒安全 |
| 重疊構造器 | 參數順序易混、擴展性差、閱讀性低 |
| Builder | 語義明確、不可變對象、支援驗證、執行緒安全易實現、可讀性高 |
「構造過程分到幾個調用中 → 可能處於不一致狀態」精確描述了分步初始化(Setter/重疊構造器)無法原子性保證對象不變約束的本質缺陷。Builder 模式將「配置階段」與「構建階段」分離,在 `build()` 瞬間原子性完成所有欄位賦值並可驗證,徹底消除中間態洩露,是構建複雜不可變對象的黃金標準。