建立Java物件時,對於可為空的屬性,建立物件的時候有3種模式:重疊構造器模式、JavaBeans模式、Builder模式(推薦)、Stream模式(推薦)。
重疊構造器模式
該模式使用多個構造器建立Java物件。
該寫法符合Java的標準慣例,但是隨著引數的越來越多,程式碼變得越來越難寫。而且該方式靈活性低,可讀性較差,客戶端想要知道哪些值的具體含義,還需要仔細數引數,而且還容易寫錯引數的位置。
package effectiveJava.builder; public class NutritionFactsMultiContr { //食物尺寸 private int servingSize; //食物數量 private int servings; //熱量(卡路里) private int calories; //脂肪含量 private int fat; //食用鹽(鈉)含量 private int sodium; //糖類含量 private int carbohydrate; public NutritionFactsMultiContr(int servingSize, int servings) { this(servingSize,servings,0); } public NutritionFactsMultiContr(int servingSize, int servings, int calories) { this(servingSize,servings,calories,0); } public NutritionFactsMultiContr(int servingSize, int servings, int calories, int fat) { this(servingSize,servings,calories,fat,0); } public NutritionFactsMultiContr(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize,servings,calories,fat,sodium,0); } public NutritionFactsMultiContr(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } public static void main(String[] args) { NutritionFactsMultiContr nutritionFacts = new NutritionFactsMultiContr(1,2,3,4,5,6); } }
JavaBeans模式
該模式通過呼叫一個無參構造器來建立物件,並使用setter方法來設定引數值。
該模式彌補了重疊構造器模式的不足,而且創造例項很容易,程式碼的可讀性也高。但是JavaBeans模式將構造過程分到幾個步驟中,在構造過程中JavaBean可能處於不一致的狀態,導致一些未知的錯誤。同時,JavaBeans模式阻止了把類做成不可變的可能。
package effectiveJava.builder; //營養成分 public class NutritionFactsSetter { //食物尺寸 private int servingSize; //食物數量 private int servings; //熱量(卡路里) private int calories; //脂肪含量 private int fat; //食用鹽(鈉)含量 private int sodium; //糖類含量 private int carbohydrate; public void setServingSize(int servingSize) { this.servingSize = servingSize; } public void setServings(int servings) { this.servings = servings; } public void setCalories(int calories) { this.calories = calories; } public void setFat(int fat) { this.fat = fat; } public void setSodium(int sodium) { this.sodium = sodium; } public void setCarbohydrate(int carbohydrate) { this.carbohydrate = carbohydrate; } public static void main(String[] args) { NutritionFactsSetter nutritionFacts = new NutritionFactsSetter(); nutritionFacts.setCalories(1); nutritionFacts.setCarbohydrate(2); } }
Builder模式
該模式不直接生成想要的物件,而是先建立一個builder物件,再在builder上呼叫類似setter的方法設定引數值,最後呼叫無參的build方法來生成所需要的物件。(注意:Builder模式下,想要建立的物件的構造器是私有的,這樣建立出來的物件是不可變的)
該模式既能像重疊構造器模式那樣的安全性,也能保證像JavaBeans模式那樣的可讀性,同時實現了物件的不可變性。但是Builder模式為了建立物件,必須先建立它的構建器,導致程式碼有點冗餘。
Builder模式模擬了具名的可選引數。
package effectiveJava.builder; //營養成分 public class NutritionFacts { //食物尺寸 private int servingSize; //食物數量 private int servings; //熱量(卡路里) private int calories; //脂肪含量 private int fat; //食用鹽(鈉)含量 private int sodium; //糖類含量 private int carbohydrate; /** * NutritionFacts是不可變的,不對外提供構造器 * @param builder */ 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 int servingSize; private int servings; private int calories; private int fat; private int sodium; private int carbohydrate; //必填欄位 public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } //可選欄位 public Builder calories(int val){ this.calories = val; return this; } public Builder fat(int val){ this.fat = val; return this; } public Builder sodium(int val){ this.sodium = val; return this; } public Builder carbohydrate(int val){ this.carbohydrate = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } public static void main(String[] args) { NutritionFacts facts = new NutritionFacts.Builder(1, 2).calories(3).fat(4).build(); } }
Stream模式
Java8中引入了一種新特性Stream,這讓編碼變得更加簡單易讀(其實,該思想在以前的JDK版本中也有使用,例如:StringBuilder、StringBuffer)。在物件的建立過程中,我們也可以使用Stream思想。
Stream模式就是將setter方法的返回值變為物件本身,這樣就可以連續呼叫setter方法。
package effectiveJava.builder; public class NutritionFactsStream { //食物尺寸 private int servingSize; //食物數量 private int servings; //熱量(卡路里) private int calories; //脂肪含量 private int fat; //食用鹽(鈉)含量 private int sodium; //糖類含量 private int carbohydrate; public NutritionFactsStream() { } public int getServingSize() { return servingSize; } public NutritionFactsStream setServingSize(int servingSize) { this.servingSize = servingSize; return this; } public int getServings() { return servings; } public NutritionFactsStream setServings(int servings) { this.servings = servings; return this; } public int getCalories() { return calories; } public NutritionFactsStream setCalories(int calories) { this.calories = calories; return this; } public int getFat() { return fat; } public NutritionFactsStream setFat(int fat) { this.fat = fat; return this; } public int getSodium() { return sodium; } public NutritionFactsStream setSodium(int sodium) { this.sodium = sodium; return this; } public int getCarbohydrate() { return carbohydrate; } public NutritionFactsStream setCarbohydrate(int carbohydrate) { this.carbohydrate = carbohydrate; return this; } public static void main(String[] args) { NutritionFactsStream nutritionFacts = new NutritionFactsStream().setServings(1).setServings(2); } }
此外,建立物件的模式還有很多,比如:工廠模式、單例模式等,不是本文討論的重點,有興趣的可以自己研究一下。
參考資料:
- Joshua Bloch 《Effective Java》