靜態(tài)工廠和構(gòu)造器有一個(gè)共同的局限性:它們都不能很好地?cái)U(kuò)展到大量的可選參數(shù)。如果一個(gè)構(gòu)造器的參數(shù)有10,11,12,...或更多時(shí)。一長串類型相同的參數(shù)會(huì)導(dǎo)致一些微妙的錯(cuò)誤,如果不小心顛倒了其中兩個(gè)參數(shù)的順序,編譯器也不會(huì)出錯(cuò),但是程序在運(yùn)行時(shí)會(huì)出現(xiàn)錯(cuò)誤的行為。
遇到許多構(gòu)造參數(shù)的時(shí)候,還有第二種代替辦法,及JavaBean模式,在這種模式下調(diào)用一個(gè)無慘構(gòu)造器來創(chuàng)建對(duì)象,調(diào)用setter方法來設(shè)置每個(gè)必要的參數(shù),以及每個(gè)相關(guān)的可選參數(shù)。
Effective Java一書中提到:JavaBean模式自身有著很嚴(yán)重的缺點(diǎn),因?yàn)闃?gòu)造過程分到了幾個(gè)調(diào)用中,在構(gòu)造過程中JavaBean可能處于不一致的狀態(tài)。類無法僅僅通過檢驗(yàn)構(gòu)造器參數(shù)的有效性來保證一致性。視圖使用處于不一致的對(duì)象,將會(huì)導(dǎo)致失敗。這種失敗與包含錯(cuò)誤的代碼大相徑庭。與此相關(guān)的另一點(diǎn)不足在于,JavaBean模式阻止了把類做成不可變的可能,這就需要程序員付出額外的努力來確保它的線程是安全的。
附注:個(gè)人在使用中,還沒有發(fā)現(xiàn)這個(gè)問題,如有了解的大神,還請(qǐng)不吝賜教。
最佳替代方法Builder模式,既能保證像重疊構(gòu)造器模式那樣的安全性,也能保證像JavaBean模式那么好的可讀性。Builder模式,不直接生成想要的對(duì)象,而是讓客戶端在builder對(duì)象上調(diào)用類似于setter的方法,來深圳每個(gè)相關(guān)的可選參數(shù)。最后,客戶端調(diào)用無參的builder方法生成不可變的對(duì)象。這個(gè)builder是它構(gòu)建的類的靜態(tài)成員類。
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 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; }
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;
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
calories(100).sodium(35).carbohydrate(27).build();
}
}
此處Builder類作為一個(gè)靜態(tài)內(nèi)部類。我們最終要獲得的是NutritionFacts對(duì)象,從他的構(gòu)造函數(shù)可以看出,是通過builder對(duì)象來對(duì)他的屬性進(jìn)行初始化的。而builder對(duì)象的屬性是通過多個(gè)setter方法設(shè)置的。