Effective Java 學習筆記(2)

 有時,我們在寫一個構造函數時,經常因爲它包含衆多的參數而苦惱,這時可以考慮用Builder模式來創建對象

 

如,我們要設計一個營養成份的類,包含能量,蛋白質,脂肪,鈣,鐵,鋅,維生素A, 維生素B1 ... 等,但在構造的時候,不一定每次都需要這些參數,如鈣,鐵,鋅和維生素等是可選的,爲了適應多種可能的搭配,比較原始的辦法就是採用telescoping constructor模式,例子如下。

 

public class Nutrition

{

          private int calories;

          private int protein;

          private int fat;

          private int ca;

          private int fe;

          private int Va;

          private int Vb;

          ...

 

          public Nutrition(int cal, int pro){...}

          public Nutrition(int cal, int pro, int fat){...}

          public Nutrition(int cal, int pro, int fat, int ca){...}

          public Nutrition(int cal, int pro, int fat, int ca, int fe){...}

          public Nutrition(int cal, int pro, int fat, int ca, int fe, int ){...}

          public Nutrition(int cal, int pro, int fat, int ca, int fe, int Va){...}

           .......

}

 

這種方法的缺點很明顯,一個函數的參數一旦超過3個,用戶就很容易把順序搞混,而更杯具的是這種情況編譯器無法識別,非常不易查錯。

 

第二種方法是JavaBean模式。

 

         

public class Nutrition

{

          private int calories;

          private int protein;

          private int fat;

          private int ca;

          private int fe;

          private int Va;

          private int Vb;

          ...

 

          public Nutrition(){...}

         

          public setCal(int cal);

          public setPro(int pro);

          public setfat(int fat);

          public setca(int ca);

          public setfe(int fe);

          .......

}

 

這種方式的缺點是在整個構造對象的過程中,其狀態不是一致的(inconsistent state),即在創建好一個對象後,這個對象的狀態在後面的某個時候內仍然是在變化的(因爲其值發生了改變),此外,這種方法只能構造一個可變(mutable)對象,必須採用附加的方法保證線程安全。

 

 所以,書中推薦了第三種方法利用Builder來構造。

public class Nutrition

{

          private final int calories;

          private final  int protein;

          private final int fat;

          private final int ca;

          private final int fe;

          private final int Va;

          private final int Vb;

 

          public static class Builder {

                private final int calories;  //必有參數

                private final int protein;  //必有參數

    

                private final int fat;    //可選參數

                private final int ca;    //可選參數

                private final int fe;    //可選參數

                private final int Va;   //可選參數

                private final int Vb;   //可選參數

              

                public Builder(int cal,int pro) {...};

               

                public Builder fat(int fat){...};

                public Builder ca(int ca){...};

                public Builder fe(int fe){...};

              

                public Nutrition builder(Builder builder)

                {

                       return new Nutrition(this);

                 }

          }  //end of Builder

         

          private Nutrition(Builder builder)

          {

                     calories = builder.calories;

                     protein = builder.protein;

                     ca = builder.ca;

                     fe = builder.fe;

                    ....

           }

}

 

使用時可以採用以下代碼:

        Nutrition nu = new Nutrition.Builder(1,2).fat(45).ca(456).fe(4).builder();

 

其中,Builder()中的是必選參數,其他的是可選參數。

 

採用這種方式,代碼易讀易寫,參數初始化的順序也無關緊要。

 

當然,它也有缺點。它必須先構造一個Builder對象,其開銷在性能問題很關鍵的場合的是不適用的。另外,相比之下,Builder模式比前面現兩種模式更加複雜,如果不是有太多的參數的話,就沒有必要使用這種模式。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章