Java 《Effective Java 中文版 第2版》學習筆記 遇到多個構造器時要考慮用構建器
靜態工廠和構造器有個共同的局限性:它們都不能很好地擴展到大量的可選參數。
當一個類中有若幹個必選屬性和多個可選屬性時,采用重疊構造器模式、JavaBeans模式或者Builder模式,但各有優劣。
當有很多參數的時候,重疊構造器模式下客戶端代碼會很難編寫,並且仍然較難以閱讀。
JavaBeans模式調用一個無參構造器來創建對象,然後調用setter方法來設置每個必要的參數,以及每個相關的可選參數。因為構造過程被分到了幾個調用中,在構造過程中JavaBean可能處於不一致的狀態。與此相關的另一點不足在於,JavaBeans模式阻止了把類做成不可變的可能。
Builder模式不直接生成想要的對象,而是讓客戶端利用所有必要的參數調用構造器(或者靜態工廠),得到一個builder對象,然後客戶端在builder對象上調用類似於setter的方法,來設置每個相關的可選參數,客戶端最後調用無參的build方法來生成不可變的對象。這個builder是它構建的類的靜態成員類。
下面就是它的示例:
1 public class NutritionFacts { 2 private final int servingSize; 3 private final int servings; 4 private final int calories; 5 private final int fat; 6 private final int sodium; 7 private final int carbohydrate; 8 9 public static class Builder { 10 //Required parameters 11 private final int servingSize; 12 private final int servings; 13 14 // Optional parameters - initialized to default values 15 private int calories = 0; 16 private int fat = 0; 17 private int carbohydrate = 0; 18 private int sodium = 0;19 20 public Builder(int servingSize, int servings) { 21 this.servingSize = servingSize; 22 this.servings = servings; 23 } 24 25 public Builder calories(int val) { 26 calories = val; 27 return this; 28 } 29 public Builder fat(int val) { 30 fat = val; 31 return this; 32 } 33 public Builder carbohydrate(int val) { 34 carbohydrate = val; 35 return this; 36 } 37 public Builder sodium(int val) { 38 sodium = val; 39 return this; 40 } 41 42 public NutritionFacts build() { 43 return new NutritionFacts(this); 44 } 45 } 46 47 private NutritionFacts(Builder builder) { 48 servingSize = builder.servingSize; 49 servings = builder.servings; 50 calories = builder.calories; 51 fat = builder.fat; 52 sodium = builder.sodium; 53 carbohydrate = builder.carbohydrate; 54 } 55 56 public static void main(String[] args) { 57 NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). 58 calories(100).sodium(35).carbohydrate(27).build(); 59 } 60 }
它在檢驗約束時將參數從builder拷貝到對象中之後,並在對象域而不是builder域中對它們進行檢驗。如果違反了任何約束條件,build方法就應該拋出IllegalArgumentException。對多個參數強加約束條件的另一個方法是,用多個setter方法對某個約束條件必須持有的所有參數進行檢查。一旦傳遞了無效的參數,立即就會發現約束條件失敗,而不是等著調用build方法。與構造器相比,builder的微略優勢在於,builder可以有多個可變參數。構造器就像方法一樣,只能有一個可變參數。
Java中傳統的抽象工廠實現是Class對象,newInstance方法總是企圖調用類的無參構造器,這個構造器甚至可能根本不存在。Class.newInstance破壞了編譯時的異常檢查。Builder模式也存在不足。為了創建對象,必須先創建它的構建器。在十分註重性能的情況下,可能就成問題了。Builder模式還比重疊構造器模式更加冗長,因此它只在有很多參數的時候才使用,比如4個或者更多個參數。通常最好一開始就使用構建器。
如果類的構造器或者靜態工廠中具有多個參數,設計這種類時,Builder模式就是種不錯的選擇,特別是當大多數參數都是可選的時候。與使用傳統的重疊構造器模式相比,使用Builder模式的客戶端代碼將更易於閱讀和編寫,構建器也比JavaBeans更加安全。
參考資料
《Effective Java 中文版 第2版》 P9-13
Java 《Effective Java 中文版 第2版》學習筆記 遇到多個構造器時要考慮用構建器