1、建立對象的方式
靜态工廠和構造器模式:
不能很好地擴充到大量的可選參數。比如用一個類表示包裝食品外面顯示的營養成分标簽。這些标簽中有幾個域是必需的:每份的含量、每罐的含量以及每份的卡路裡。還有超過2 0 個的可選域: 總脂肪量、飽和脂肪量、轉化脂肪、膽固醇、納,等等。大多數産品在某幾個可選域中都會有非零的值。
對于這樣的類,應該用哪種構造器或者靜态工廠來編寫呢?程式員一向習慣采用重疊構造器( telescoping cons tructor )模式,在這種模式下,提供的第一個構造器隻有必要的參數,第二個構造器有一個可選參數,第三個構造器有兩個可選參數,依此類推,最後一個構造器包含所有可選的參數。
- 随着參數數目增加,它很快就失去了控制;簡而言之,重疊構造器模式可行,但是當有許多參數的時候,用戶端代碼會很難縮寫,并且仍然較難以閱讀
遇到許多可選的構造器參數的時候,還有第二種代替辦法,即
JavaBeans 模式
在這種模式下,先調用一個無參構造器來建立對象,然後再調用setter 方法來設定每個必要的參數,以及每個相關的可選參數:
因為建構過程被分到了幾個調用中,在建構過程中JavaBean可能處于不一緻的狀态。類無法僅僅通過驗證構造器參數的有效性來保持一緻性。試圖使用處于不一緻狀态的對象,将會導緻失敗。與此相關的另一點不足在于,JavaBeans模式組織了把類做成不可變的可能,這就需要程式員付出額外的努力來確定他的線程安全。
builder模式
既能保證構造器模式那樣的安全性,也能保證JavaBeans模式那麼好的可讀性;
不直接生成想要的對象,而是讓用戶端利用所有必要的參數調用構造器,得到一個builder對象,然後用戶端在builder對象上調用類似于setter方法,設定每個相關的可選參數。最後用戶端調用無參的build方法生成不可變的對象;builder是它建構的類的靜态成員類。
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 {
// 必要參數
private final int servingSize;
private final int servings;
// 可選參數
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;//傳回Builder類對象本身,以便把調用連結起來
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = 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;
}
@Override
public String toString() {
return "[" +
"servingSize:" + servingSize +
",servings:" + servings +
",calories:" + calories +
",fat:" + fat +
",sodium:" + sodium +
",carbohydrate:" + carbohydrate +
"]";
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build();
System.out.println(cocaCola);
}
}
builder像個構造器一樣,可以對其參數強加限制條件,build()方法可以檢驗這些限制條件,将參數從builder拷貝到對象之後,在對象域進行檢驗。
與構造器相比,builder優勢在于:可以有多個可變參數。
Java中傳統的抽象工廠實作Class對象。用newInstance方法充當build方法的一部分,隐含着問題:newInstance總是試圖調用類的無參構造器,若是不存在也不會收到編譯錯誤。用戶端必須在運作時處理異常,很不友善。
Class.newInstance破壞了編譯時的異常檢查,builder接口彌補了不足;
builder模式的不足:為了建立對象必須先建立它的構造器。