I want to fly higher
          programming Explorer
          posts - 114,comments - 263,trackbacks - 0
          第二條:遇到多個構(gòu)造器參數(shù)時考慮用構(gòu)建器:
             
               1.靜態(tài)工廠和構(gòu)造器有幾個共同的局限性,它們都不能很好的擴展到大量的可選參數(shù).如一個類表示包裝食品外面顯示的營養(yǎng)成分標(biāo)簽,標(biāo)簽中有些域是必須的,不過還有多個可選域。大多數(shù)產(chǎn)品在某幾個可選域中都有非0值。
               2.對于這樣的類,程序員一貫采用重疊構(gòu)造器(telescoping constructor)模式。在這種模式下,你提供第一個只有必要參數(shù)的構(gòu)造器,第二個構(gòu)造有一個可選參數(shù),第三個有兩個可選參數(shù),一次類推,最后一個構(gòu)造器包含所有可選參數(shù)。
               3.要創(chuàng)建實例的時候,就是參數(shù)列表最短的構(gòu)造器,但其包含了要設(shè)置的所有參數(shù)-->
                不過如果參數(shù)是包括一些可選,可選可能有一個為0,如
                NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);這個構(gòu)造器調(diào)用通常需要許多你根本不想設(shè)置的參數(shù),但還是不能不為他們傳值。如這里給fat傳了一個0.
                這里如果僅僅是6個參數(shù)的話,情況看起來好像不是特別糟。問題是隨著參數(shù)數(shù)目的增加,其很快就失去了控制->
               4.一句話:重疊構(gòu)造器模式可行,但是當(dāng)有許多參數(shù)的時候,客戶端代碼可能會很難編寫,并且依然難以閱讀。如果讀者想知道參數(shù)是神馬意思,必須很仔細(xì)的要數(shù)著這些參數(shù)來研究。一連串類型相同的參數(shù)會導(dǎo)致一些微妙的錯誤。如果client不小心點到了其中兩個參數(shù)的順序,編譯器也不會報錯,但是程序在運行時會出現(xiàn)錯誤的行為。
               5.遇到多構(gòu)造器參數(shù)的時候,還有第二種代替辦法,即JavaBeans模式。該模式下,調(diào)用一個無參構(gòu)造函數(shù)來創(chuàng)建對象,然后調(diào)用setter方法來設(shè)置每個必要的參數(shù),以及每個相關(guān)的可選參數(shù).
                這種模式彌補了重疊構(gòu)造器模式的不足。說的明白一點,就是創(chuàng)建實例很容易,產(chǎn)生的代碼讀起來也很容易。
                不過,JavaBeans模式自身有很嚴(yán)重的缺點。因為構(gòu)造過程被分到了幾個調(diào)用中,在構(gòu)造過程中JavaBean可能處于不一致的狀態(tài)。類無法僅僅通過檢驗構(gòu)造器參數(shù)的有效性來保證一致性。試圖使用處于不一致狀態(tài)的對象,將會導(dǎo)致失敗。這種失敗與包含錯誤的代碼大相徑庭,因為其調(diào)試起來十分困難。
                另外不足的一點在于,JavaBeans模式阻止了類做成不可變的可能,這就需要程序員付出額外的努力來保證它的線程安全。
                當(dāng)對象的構(gòu)造完成,并且不允許在解凍之前使用時,通過手動“凍結(jié)”對象,可以彌補這些不足。但是這種方式十分笨拙,在實踐中很少使用。此外,它甚至?xí)谶\行時導(dǎo)致錯誤,因為編譯器無法確保程序員會在使用之前先在對象上調(diào)用freeze方法。
                注:個人認(rèn)為freeze和鎖類似吧,即保證使用之前先鎖住對象,這樣只有當(dāng)前線程可進(jìn)行操作;使用完畢調(diào)用解凍方法?釋放鎖-->
              6.第三種替代方法,既能保證向重疊構(gòu)造器模式那樣的安全性,也能保證向JavaBeans模式那么好的可讀性。這就是Builde模式的一種形式。不直接生成想要的對象,而是讓客戶端利用所有必要的參數(shù)調(diào)用構(gòu)造器或者靜態(tài)工廠,得到一個builder對象。然后客戶端在builder上調(diào)用類似setter的方法來設(shè)置每個相關(guān)的可選參數(shù)。最后客戶端調(diào)用無參的build方法來生成不可變的對象。這個builder是它構(gòu)造的類的靜態(tài)的成員類。
               builder的setter方法返回builder本身,以便可以把調(diào)用鏈接起來,以下就是調(diào)用代碼:
               NutritionFact3 cocaCola = new NutritionFact3.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
               這樣的客戶端代碼很容易編寫,更為重要的是,易于閱讀。builder模式模擬了具名的可選參數(shù),就是Ada和python一樣。
                注:Ada是一種表現(xiàn)能力很強的通用程序設(shè)計語言,它是美國國防部為克服軟件開發(fā)危機,耗費巨資,歷時近20年研制成功的。它被譽為第四代計算機語言的成功代表。
               builder像個構(gòu)造器一樣,可以對其參數(shù)強加約束條件。build方法可以檢驗這些約束條件,將參數(shù)從builer拷貝到對象中之后,并在對象域而不是builder域中對他們進(jìn)行檢驗(:保護性拷貝:在檢查參數(shù)的有效性之前進(jìn)行,并且有效性檢查是針對拷貝之后的對象)。如果違反了任何約束條件,builder方法就應(yīng)該拋出IllegalStateException.異常的詳細(xì)信息應(yīng)該顯示出違反了哪個約束條件。
               對多個參數(shù)強加約束條件的另一種方式是:用多個setter方法對某個約束條件必須持有的所有參數(shù)進(jìn)行檢查。如果該約束條件沒有得到滿足,setter方法就會拋出IllegalArgumentException。這樣有個好處,就是一旦傳遞了無效參數(shù),立即就會發(fā)現(xiàn)約束條件失敗,而不是等著調(diào)用build方法。
               與構(gòu)造器相比,builder的略微優(yōu)勢在于,builder可以有多個可變參數(shù)。構(gòu)造器就像方法一樣,只能有一個可變參數(shù)。因為builder利用單獨的方法來設(shè)置每個參數(shù),你想要多少個可變參數(shù),他們就可以有多少個,直到每個setter方法都有一個可變參數(shù)。
               builder模式十分靈活,可以利用單個builder構(gòu)建多個對象。builder的參數(shù)可以在創(chuàng)建對象期間進(jìn)行調(diào)整,也可以隨著不同的對象兒改變。builder可以自動填充某些域,例如每次創(chuàng)建對象時自動增加序列號。
               設(shè)置了參數(shù)的builder生成了一個很好的抽象工廠,即客戶端可將將這樣一個builder傳給方法,使得該方法能夠為客戶端創(chuàng)建一個或多個對象。要使用該方法,需要有一個類型表示builder。1.5或者更新版本,用泛型即能滿足所有的builder,無論他們在構(gòu)建哪種類型的對象。
               如:
               public interface Builder<T>
               {
                public T builder();
               }
               如,可以聲明NutritionFact3.Builder類來實現(xiàn)Builder<NutritionFact3>
               帶有Builder實例的方法通常利用有限制的通配符類型來約束構(gòu)建器的類型參數(shù),如:
                Tree buildTree(Builder<? extends Node> nodeBuilder),構(gòu)建每個節(jié)點的方法,利用一個客戶端提供的Builder是來來構(gòu)建樹。
               Java中傳統(tǒng)的抽象工廠實現(xiàn)是Class對象,用newInstance方法充當(dāng)build方法的一部分。這種用法隱含著一些問題。newInstance方法總是企圖調(diào)用類的無參構(gòu)造器,這個構(gòu)造器可能根本不存在。如果來沒有可以訪問的無參構(gòu)造器,你也不會收到編譯錯誤。相反,客戶端代碼必須在運行時處理InstaniationException或者IlleagalAccessExcepion.這樣既不雅觀,也不方便。newInstance方法還會傳播由無參構(gòu)造器拋出的任何異常,及時newInstance缺乏相應(yīng)的throws子句。換句話說,Class.newInstance破壞了編譯時的異常檢查,而Builder接口彌補了這些不足。
               7.Builder模式確實也有其自身不足。為了創(chuàng)建對象,必須先創(chuàng)建它的構(gòu)建器。雖然創(chuàng)建構(gòu)建器的開銷在實踐中可能不那么明顯,但是在某些十分注重性能的情況下,可能就成問題了。Builder模式可能還比重疊構(gòu)造器模式更加冗長,因為它只有在很多參數(shù)的時候才使用,比如4個或者跟更多個參數(shù)。
               不過記住,你將來可能需要添加參數(shù)。如果一開始就是用構(gòu)造器或者靜態(tài)工廠,等到類需要多個參數(shù)時才添加構(gòu)建器,就會無法控制。那些過時的構(gòu)造器或者靜態(tài)工廠顯得十分不協(xié)調(diào)。因此通常最好一開始就是構(gòu)建器。
               簡而言之,如果來的構(gòu)造器或者靜態(tài)工廠中有多個參數(shù)。設(shè)計這種類時,Builder模式是中不錯的選擇。 特別是當(dāng)大多數(shù)參數(shù)都是可選的時候。與是用傳統(tǒng)的重疊構(gòu)造器模式相比,是用Builder模式的客戶端代碼更易于閱讀和編寫,構(gòu)建器也比JavaBeans更安全。

          部分源碼:
          package com.book.chap2.builder;

          /**
          * 營養(yǎng)成分標(biāo)簽
          * <p>
          * 針對多個可選參數(shù)的構(gòu)造器,采用的重疊構(gòu)造器模式(telescoping constructor)
          *
          * @author landon
          * @since 1.6.0_35
          * @version 1.0.0 2013-1-8
          *
          */


          public class NutritionFacts
          {
             
          /** 分量,必須 */
             
          private final int servingSize;// required
              /** 一份分量,必須 */
             
          private final int servings;// required
              /** 卡路里,可選 */
             
          private final int calories;// optional
              /** 脂肪含量 ,可選 */
             
          private final int fat;// optional
              /** 鈉含量 ,可選 */
             
          private final int sodium;// optional
              /** 碳水化合物 ,可選 */
             
          private final int carbohydrate;// optional

             
          /**
               *
               * 構(gòu)造函數(shù)1,2個必須成分作為參數(shù)
               *
               * @param servingSize
               * @param servings
              
          */

             
          public NutritionFacts(int servingSize, int servings)
             
          {
                 
          this(servingSize, servingSize, 0);
              }


             
          /**
               *
               * 構(gòu)造函數(shù)2,增加一個可選成分:卡路里
               *
               * @param servingSize
               * @param servings
               * @param calories
              
          */

             
          public NutritionFacts(int servingSize, int servings, int calories)
             
          {
                 
          this(servingSize, servings, calories, 0);
              }


             
          /**
               *
               * 構(gòu)造函數(shù)3,又增加一個可選成分:脂肪
               *
               * @param servingSize
               * @param servings
               * @param calories
               * @param fat
              
          */

             
          public NutritionFacts(int servingSize, int servings, int calories, int fat)
             
          {
                 
          this(servingSize, servings, calories, fat, 0);
              }


             
          /**
               *
               * 構(gòu)造函數(shù)4,繼續(xù)增加一個可選成分:鈉
               *
               * @param servingSize
               * @param servings
               * @param calories
               * @param fat
               * @param sodium
              
          */

             
          public NutritionFacts(int servingSize, int servings, int calories,
                     
          int fat, int sodium)
             
          {
                 
          this(servingSize, servings, calories, fat, sodium, 0);
              }


             
          /**
               *
               * 構(gòu)造函數(shù)5,擁有全部成分
               *
               * @param servingSize
               * @param servings
               * @param calories
               * @param fat
               * @param sodium
               * @param carbohydrate
              
          */

             
          public NutritionFacts(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(Stringargs)
             
          {
                 
          //fat參數(shù)傳了一個0
                  NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
              }

          }


          package com.book.chap2.builder;

          /**
          *
          * 營養(yǎng)成分標(biāo)簽
          * <p>
          * 利用JavaBeans模式
          *
          *@author landon
          *@since 1.6.0_35
          *@version 1.0.0 2013-1-8
          *
          */


          public class NutritionFacts2
          {
             
          private  int servingSize = -1;
             
          private  int servings = -1;
             
          private  int calories = 0;
             
          private  int fat = 0;
             
          private  int sodium = 0;
             
          private  int carbohydrate = 0;
             
             
          //無參構(gòu)造函數(shù)
              public NutritionFacts2()
             
          {
              }


             
          //Setters
             
             
          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(Stringargs)
             
          {
                  NutritionFacts2 cocaCola = new NutritionFacts2();
                 
                 
          //利用setters
                  cocaCola.setServingSize(240);
                  cocaCola.setServings(8);
                  cocaCola.setCalories(100);
                  cocaCola.setSodium(35);
                  cocaCola.setCarbohydrate(27);
              }

          }


          package com.book.chap2.builder;

          /**
          *
          * 營養(yǎng)成分標(biāo)簽
          * <p>
          * 使用buider構(gòu)建器模式來構(gòu)造多參數(shù)的類
          *
          * @author landon
          * @since 1.6.0_35
          * @version 1.0.0 2013-1-8
          *
          */


          public class NutritionFact3
          {
             
          //均是final,不可變
             
             
          private final int servingSize;// required
              private final int servings;// required
              private final int calories;// optional
              private final int fat;// optional
              private final int sodium;// optional
              private final int carbohydrate;// optional

             
          public static class Builder
             
          {
                 
          // 必須參數(shù)
                  private final int servingSize;// required
                  private final int servings;// required

                 
          // 可選參數(shù)
                  private int calories = 0;// optional
                  private int fat = 0;// optional
                  private int sodium = 0;// optional
                  private int carbohydrate = 0;// optional

                 
          // 必須參數(shù)必須通過通過構(gòu)造參數(shù)傳遞
                  public Builder(int servingSize, int servings)
                 
          {
                     
          this.servingSize = servingSize;
                     
          this.servings = servings;
                  }


                 
          // 構(gòu)建calories,返回本身,以便可以把調(diào)用連接起來
                  public Builder calories(int calories)
                 
          {
                     
          this.calories = calories;
                     
          return this;
                  }


                 
          // 構(gòu)建sodium
                  public Builder sodium(int sodium)
                 
          {
                     
          this.sodium = sodium;
                     
          return this;
                  }


                 
          // 構(gòu)建fat
                  public Builder fat(int fat)
                 
          {
                     
          this.fat = fat;
                     
          return this;
                  }


                 
          // 構(gòu)建carbohydrate
                  public Builder carbohydrate(int carbohydrate)
                 
          {
                     
          this.carbohydrate = carbohydrate;
                     
          return this;
                  }


                 
          //build,返回NutritionFact3
                  public NutritionFact3 build()
                 
          {
                     
          return new NutritionFact3(this);
                  }

              }


             
          // 隱藏構(gòu)造函數(shù)
              private NutritionFact3(Builder builder)
             
          {
                  servingSize
          = builder.servingSize;
                  servings
          = builder.servings;
                  calories
          = builder.calories;
                  fat
          = builder.fat;
                  sodium
          = builder.sodium;
                  carbohydrate
          = builder.carbohydrate;
              }

             
             
          public static void main(Stringargs)
             
          {
                  NutritionFact3 cocaCola
          = new NutritionFact3.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
              }

          }

          posted on 2013-03-15 15:21 landon 閱讀(2317) 評論(0)  編輯  收藏 所屬分類: ProgramBook
          主站蜘蛛池模板: 扶绥县| 西城区| 库车县| 南陵县| 民勤县| 桐乡市| 佛冈县| 大兴区| 客服| 开封县| 合阳县| 阜阳市| 微山县| 会昌县| 临沧市| 萨迦县| 额尔古纳市| 水富县| 贵阳市| 蕲春县| 云霄县| 凌源市| 无极县| 应用必备| 罗甸县| 宁都县| 敦煌市| 驻马店市| 新泰市| 康定县| 汤原县| 连州市| 大兴区| 嘉定区| 大荔县| 沾益县| 雅安市| 伊春市| 泰顺县| 庆云县| 长沙县|