Effective Java 隨筆——第2條:遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用構(gòu)建器

靜態(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è)置的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容