<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## Chapter 2. Creating and Destroying Objects(創建和銷毀對象) ### Item 2: Consider a builder when faced with many constructor parameters(當構造函數有多個參數時,考慮改用構建器) Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields—serving size, servings per container, and calories per serving— and more than twenty optional fields—total fat, saturated fat, trans fat,cholesterol, sodium, and so on. Most products have nonzero values for only a few of these optional fields. 靜態工廠和構造函數都有一個局限:它們不能對大量可選參數做很好的擴展。以一個類為例,它表示包裝食品上的營養標簽。這些標簽上有一些字段是必需的,如:凈含量、毛重和每單位份量的卡路里,另有超過 20 個可選的字段,如:總脂肪、飽和脂肪、反式脂肪、膽固醇、鈉等等。大多數產品只有這些可選字段中的少數,且具有非零值。 What sort of constructors or static factories should you write for such a class?Traditionally, programmers have used the telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a single optional parameter, a third with two optional parameters, and so on,culminating in a constructor with all the optional parameters. Here’s how it looks in practice. For brevity’s sake, only four optional fields are shown: 應該為這樣的類編寫什么種類的構造函數或靜態工廠呢?傳統的方式是使用可伸縮構造函數,在這種模式中,只向構造函數提供必需的參數。即,向第一個構造函數提供單個可選參數,向第二個構造函數提供兩個可選參數,以此類推,最后一個構造函數是具有所有可選參數的。這是它在實際應用中的樣子。為了簡潔起見,只展示具備四個可選字段的情況: ``` // Telescoping constructor pattern - does not scale well! public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // (per serving) optional private final int fat; // (g/serving) optional private final int sodium; // (mg/serving) optional private final int carbohydrate; // (g/serving) optional public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); } 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; } } ``` When you want to create an instance, you use the constructor with the shortest parameter list containing all the parameters you want to set: 當你想要創建一個實例時,可以使用包含所需參數的最短參數列表的構造函數: ``` NutritionFacts cocaCola =new NutritionFacts(240, 8, 100, 0, 35, 27); ``` Typically this constructor invocation will require many parameters that you don’t want to set, but you’re forced to pass a value for them anyway. In this case,we passed a value of 0 for fat. With “only” six parameters this may not seem so bad, but it quickly gets out of hand as the number of parameters increases. 通常,這個構造函數包含許多額外的參數,但是你必須為它們傳遞一個值。在本例中,我們為 fat 傳遞了一個值 0。只有六個參數時,這可能看起來不那么糟,但隨著參數的增加,它很快就會失控。 In short, **the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.** The reader is left wondering what all those values mean and must carefully count parameters to find out. Long sequences of identically typed parameters can cause subtle bugs. If the client accidentally reverses two such parameters, the compiler won’t complain, but the program will misbehave at runtime (Item 51). 簡單地說,**可伸縮構造函數模式可以工作,但是當有很多參數時,編寫客戶端代碼是很困難的,而且讀起來更困難。** 讀者想知道所有這些值是什么意思,必須仔細清點參數。相同類型參數的長序列會導致細微的錯誤。如果客戶端不小心倒轉了兩個這樣的參數,編譯器不會報錯,但是程序會在運行時出錯([Item-51](/Chapter-8/Chapter-8-Item-51-Design-method-signatures-carefully.md))。 A second alternative when you’re faced with many optional parameters in a constructor is the JavaBeans pattern, in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest: 當你在構造函數中遇到許多可選參數時,另一種選擇是 JavaBean 模式,在這種模式中,你調用一個無參數的構造函數來創建對象,然后調用 setter 方法來設置每個所需的參數和每個感興趣的可選參數: ``` // JavaBeans Pattern - allows inconsistency, mandates mutability public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // Required; no default value private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } } ``` This pattern has none of the disadvantages of the telescoping constructor pattern.It is easy, if a bit wordy, to create instances, and easy to read the resulting code: 這個模式沒有可伸縮構造函數模式的缺點。創建實例很容易,雖然有點冗長,但很容易閱讀生成的代碼: ``` NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); ``` Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction. The class does not have the option of enforcing consistency merely by checking the validity of the constructor parameters. Attempting to use an object when it’s in an inconsistent state may cause failures that are far removed from the code containing the bug and hence difficult to debug. A related disadvantage is that the JavaBeans pattern precludes the possibility of making a class immutable (Item 17) and requires added effort on the part of the programmer to ensure thread safety. 不幸的是,JavaBean 模式本身有嚴重的缺點。因為構建是在多個調用之間進行的,所以 JavaBean 可能在構建的過程中處于不一致的狀態。該類不能僅通過檢查構造函數參數的有效性來強制一致性。在不一致的狀態下嘗試使用對象可能會導致錯誤的發生,而包含這些錯誤的代碼很難調試。一個相關的缺點是,JavaBean 模式排除了使類不可變的可能性([Item-17](/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md)),并且需要程序員額外的努力來確保線程安全。 It is possible to reduce these disadvantages by manually “freezing” the object when its construction is complete and not allowing it to be used until frozen, but this variant is unwieldy and rarely used in practice. Moreover, it can cause errors at runtime because the compiler cannot ensure that the programmer calls the freeze method on an object before using it. 通過在對象構建完成時手動「凍結」對象,并在凍結之前不允許使用對象,可以減少這些缺陷,但是這種變通方式很笨拙,在實踐中很少使用。此外,它可能在運行時導致錯誤,因為編譯器不能確保程序員在使用對象之前調用它的 freeze 方法。 Luckily, there is a third alternative that combines the safety of the telescoping constructor pattern with the readability of the JavaBeans pattern. It is a form of the Builder pattern [Gamma95]. Instead of making the desired object directly,the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object, which is typically immutable. The builder is typically a static member class (Item 24) of the class itbuilds. Here’s how it looks in practice: 幸運的是,還有第三種選擇,它結合了可伸縮構造函數模式的安全性和 JavaBean 模式的可讀性。它是建造者模式的一種形式 [Gamma95]。客戶端不直接生成所需的對象,而是使用所有必需的參數調用構造函數(或靜態工廠),并獲得一個 builder 對象。然后,客戶端在構建器對象上調用像 setter 這樣的方法來設置每個感興趣的可選參數。最后,客戶端調用一個無參數的構建方法來生成對象,這通常是不可變的。構建器通常是它構建的類的靜態成員類([Item-24](/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。下面是它在實際應用中的樣子: **若將該案例「構建機制」獨立出來,或能廣泛適應相似結構的構建需求,詳見文末隨筆** ``` // Builder Pattern 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 sodium = 0; private int carbohydrate = 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 sodium(int val) { sodium = val; return this; } public Builder carbohydrate(int val) { carbohydrate = 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; } } ``` The NutritionFacts class is immutable, and all parameter default values are in one place. The builder’s setter methods return the builder itself so that invocations can be chained, resulting in a fluent API. Here’s how the client code looks: NutritionFacts 類是不可變的,所有參數默認值都在一個位置。構建器的 setter 方法返回構建器本身,這樣就可以鏈式調用,從而得到一個流暢的 API。下面是客戶端代碼的樣子: ``` NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build(); ``` This client code is easy to write and, more importantly, easy to read. The Builder pattern simulates named optional parameters as found in Python and Scala. 該客戶端代碼易于編寫,更重要的是易于閱讀。建造者模式模擬 Python 和 Scala 中的可選參數。 Validity checks were omitted for brevity. To detect invalid parameters as soon as possible, check parameter validity in the builder’s constructor and methods.Check invariants involving multiple parameters in the constructor invoked by the build method. To ensure these invariants against attack, do the checks on object fields after copying parameters from the builder (Item 50). If a check fails, throw an IllegalArgumentException (Item 72) whose detail message indicates which parameters are invalid (Item 75). 為了簡潔,省略了有效性檢查。為了盡快檢測無效的參數,請檢查構建器的構造函數和方法中的參數有效性。檢查構建方法調用的構造函數中涉及多個參數的不變量。為了確保這些不變量不受攻擊,在從構建器復制參數之后檢查對象字段([Item-50](/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。如果檢查失敗,拋出一個 IllegalArgumentException([Item-72](/Chapter-10/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md)),它的詳細消息指示哪些參數無效([Item-75](/Chapter-10/Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md))。 The Builder pattern is well suited to class hierarchies. Use a parallel hierarchy of builders, each nested in the corresponding class. Abstract classes have abstract builders; concrete classes have concrete builders. For example,consider an abstract class at the root of a hierarchy representing various kinds of pizza: 建造者模式非常適合于類層次結構。使用構建器的并行層次結構,每個構建器都嵌套在相應的類中。抽象類有抽象類構建器;具體類有具體類構建器。例如,考慮一個在層次結構處于最低端的抽象類,它代表各種比薩餅: ``` import java.util.EnumSet; import java.util.Objects; import java.util.Set; // Builder pattern for class hierarchies public abstract class Pizza { public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE} final Set<Topping> toppings; abstract static class Builder<T extends Builder<T>> { EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class); public T addTopping(Topping topping) { toppings.add(Objects.requireNonNull(topping)); return self(); } abstract Pizza build(); // Subclasses must override this method to return "this" protected abstract T self(); } Pizza(Builder<?> builder) { toppings = builder.toppings.clone(); // See Item 50 } } ``` Note that Pizza.Builder is a generic type with a recursive type parameter (Item 30). This, along with the abstract self method, allows method chaining to work properly in subclasses, without the need for casts. This workaround for the fact that Java lacks a self type is known as the simulated self-type idiom. Here are two concrete subclasses of Pizza, one of which represents a standard New-York-style pizza, the other a calzone. The former has a required size parameter, while the latter lets you specify whether sauce should be inside or out: 請注意,`Pizza.Builder` 是具有遞歸類型參數的泛型類型([Item-31](/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))。這與抽象 self 方法一起,允許方法鏈接在子類中正常工作,而不需要強制轉換。對于 Java 缺少自類型這一事實,這種變通方法稱為模擬自類型習慣用法。這里有兩個具體的比薩子類,一個是標準的紐約風格的比薩,另一個是 calzone。前者有一個必需的尺寸大小參數,而后者讓你指定醬料應該放在里面還是外面: ``` import java.util.Objects; public class NyPizza extends Pizza { public enum Size {SMALL, MEDIUM, LARGE} private final Size size; public static class Builder extends Pizza.Builder<Builder> { private final Size size; public Builder(Size size) { this.size = Objects.requireNonNull(size); } @Override public NyPizza build() { return new NyPizza(this); } @Override protected Builder self() { return this; } } private NyPizza(Builder builder) { super(builder); size = builder.size; } } public class Calzone extends Pizza { private final boolean sauceInside; public static class Builder extends Pizza.Builder<Builder> { private boolean sauceInside = false; // Default public Builder sauceInside() { sauceInside = true; return this; } @Override public Calzone build() { return new Calzone(this); } @Override protected Builder self() { return this; } } private Calzone(Builder builder) { super(builder); sauceInside = builder.sauceInside; } } ``` Note that the build method in each subclass’s builder is declared to return the correct subclass: the build method of NyPizza.Builder returns NyPizza, while the one in Calzone.Builder returns Calzone. This technique, wherein a subclass method is declared to return a subtype of the return type declared in the super-class, is known as covariant return typing. It allows clients to use these builders without the need for casting.The client code for these “hierarchical builders” is essentially identical to the code for the simple NutritionFacts builder. The example client code shown next assumes static imports on enum constants for brevity: 注意,每個子類的構建器中的構建方法聲明為返回正確的子類:構建的方法 `NyPizza.Builder` 返回 NyPizza,而在 `Calzone.Builder` 則返回 Calzone。這種技術稱為協變返回類型,其中一個子類方法聲明為返回超類中聲明的返回類型的子類型。它允許客戶使用這些構建器,而不需要強制轉換。這些「層次構建器」的客戶端代碼與簡單的 NutritionFacts 構建器的代碼基本相同。為簡潔起見,下面顯示的示例客戶端代碼假定枚舉常量上的靜態導入: ``` NyPizza pizza = new NyPizza.Builder(SMALL) .addTopping(SAUSAGE).addTopping(ONION).build(); Calzone calzone = new Calzone.Builder() .addTopping(HAM).sauceInside().build(); ``` A minor advantage of builders over constructors is that builders can have multiple varargs parameters because each parameter is specified in its own method. Alternatively, builders can aggregate the parameters passed into multiple calls to a method into a single field, as demonstrated in the addTopping method earlier. 與構造函數相比,構建器的一個小優點是構建器可以有多個變量參數,因為每個參數都是在自己的方法中指定的。或者,構建器可以將傳遞給一個方法的多個調用的參數聚合到單個字段中,如前面的 addTopping 方法中所示。 The Builder pattern is quite flexible. A single builder can be used repeatedly to build multiple objects. The parameters of the builder can be tweaked between invocations of the build method to vary the objects that are created. A builder can fill in some fields automatically upon object creation, such as a serial number that increases each time an object is created. 建造者模式非常靈活。一個構建器可以反復構建多個對象。構建器的參數可以在構建方法的調用之間進行調整,以改變創建的對象。構建器可以在創建對象時自動填充某些字段,例如在每次創建對象時增加的序列號。 The Builder pattern has disadvantages as well. In order to create an object,you must first create its builder. While the cost of creating this builder is unlikely to be noticeable in practice, it could be a problem in performance-critical situations. Also, the Builder pattern is more verbose than the telescoping constructor pattern, so it should be used only if there are enough parameters to make it worthwhile, say four or more. But keep in mind that you may want to add more parameters in the future. But if you start out with constructors or static factories and switch to a builder when the class evolves to the point where the number of parameters gets out of hand, the obsolete constructors or static factories will stick out like a sore thumb. Therefore, it’s often better to start with a builder in the first place. 建造者模式也有缺點。為了創建一個對象,你必須首先創建它的構建器。雖然在實際應用中創建這個構建器的成本可能并不顯著,但在以性能為關鍵的場景下,這可能會是一個問題。而且,建造者模式比可伸縮構造函數模式更冗長,因此只有在有足夠多的參數時才值得使用,比如有 4 個或更多參數時,才應該使用它。但是請記住,你可能希望在將來添加更多的參數。但是,如果你以構造函數或靜態工廠開始,直至類擴展到參數數量無法控制的程度時,也會切換到構建器,但是過時的構造函數或靜態工廠將很難處理。因此,最好一開始就從構建器開始。 In summary, the Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if many of the parameters are optional or of identical type. Client code is much easier to read and write with builders than with telescoping constructors, and builders are much safer than JavaBeans. 總之,在設計構造函數或靜態工廠的類時,建造者模式是一個很好的選擇,特別是當許多參數是可選的或具有相同類型時。與可伸縮構造函數相比,使用構建器客戶端代碼更容易讀寫,而且構建器比 JavaBean 更安全。 ### **隨筆** 每個內部 Builder 類要對每個字段建立相應方法,代碼比較冗長。若將「構建機制」獨立出來,或能廣泛適應相似結構的構建需求。以下是針對原文案例的簡要修改,僅供參考: ``` class EntityCreator<T> { private Class<T> classInstance; private T entityObj; public EntityCreator(Class<T> classInstance, Object... initParams) throws Exception { this.classInstance = classInstance; Class<?>[] paramTypes = new Class<?>[initParams.length]; for (int index = 0, length = initParams.length; index < length; index++) { String checkStr = initParams[index].getClass().getSimpleName(); if (checkStr.contains("Integer")) { paramTypes[index] = int.class; } if (checkStr.contains("Double")) { paramTypes[index] = double.class; } if (checkStr.contains("Boolean")) { paramTypes[index] = boolean.class; } if (checkStr.contains("String")) { paramTypes[index] = initParams[index].getClass(); } } Constructor<T> constructor = classInstance.getDeclaredConstructor(paramTypes); constructor.setAccessible(true); this.entityObj = constructor.newInstance(initParams); } public EntityCreator<T> setValue(String paramName, Object paramValue) throws Exception { Field field = classInstance.getDeclaredField(paramName); field.setAccessible(true); field.set(entityObj, paramValue); return this; } public T build() { return entityObj; } } ``` 如此,可移除整個內部 Builder 類,NutritionFacts 類私有構造的參數僅包括兩個必填的 servingSize、servings 字段: ``` public class NutritionFacts { // 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 sodium = 0; private int carbohydrate = 0; private NutritionFacts(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } } ``` 該案例的客戶端代碼改為: ``` NutritionFacts cocaCola = new EntityCreator<>(NutritionFacts.class, 240, 8) .setValue("calories", 100) .setValue("sodium", 35) .setValue("carbohydrate", 27).build(); ``` --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-2/Chapter-2-Introduction.md)** - **Previous Item(上一條目):[Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)](/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md)** - **Next Item(下一條目):[Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)](/Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md)**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看