二、當構造方法參數過多時使用build模式
靜態工廠方法和構造方法都有同一個缺陷:當可選參數過多時,它們都沒有辦法很好的進行擴展。所以,當參數過多時,一般采用的方法有:可伸縮構造方法模式(the telescoping constructor pattern)、javaBeans模式和Builder模式。
1、telescoping constructor模式
(1)如何使用
在telescoping constructor模式中,首先提供一個必需參數的構造方法,然後再加一個可選參數形成第二個構造方法,接著再加一個可選參數形成第三個構造方法,等等,直到構造方法中包含所有的可選參數。例如:
1 public classPerson { 2 private String name; // 必需 3 private String nickname; // 必需 4 private int age; // 可選 5 private String phone; // 可選 6 7 public Person(String name, String nickname) { 8 this(name, nickname, 0); 9 } 10 11 public Person(String name, String nickname, intage) { 12 this(name, nickname, age, null); 13 } 14 15 public Person(String name, String nickname, int age, String phone) { 16 this.name = name; 17 this.nickname = nickname; 18 this.age = age; 19 this.phone = phone; 20 } 21 }
實例化方法:Person p = new Person("張三", "小小");
(2)缺點
-
- 當對象實例化時,你可能會需要對你不想設置的參數傳值
- 當參數很多時,客戶端代碼很難編寫,也很難進行閱讀
2、JavaBeans模式
(1)如何使用
在JavaBeans模式中,先調用一個無參構造方法創建對象,然後調用類中的setter方法設置參數。例如:
1 public class Person { 2 private String name; // 必需 3 private String nickname; // 必需 4 private int age; // 可選 5 private String phone; // 可選 6 7 public Person() {} 8 9 public void setName(String name) { 10 this.name = name; 11 } 12 13 public void setNickname(String nickname) { 14 this.nickname = nickname; 15 } 16 17 public void setAge(int age) { 18 this.age = age; 19 } 20 21 public void setPhone(String phone) { 22 this.phone = phone; 23 } 24 }
實例化方法: Person p = new Person();
p.setName("張三");
p.setNickname("小小");
(2)缺點
由於構造方法在多次調用中被分割,所以JavaBeans模式是線程不安全的,在多線程模式下,對象的值可能會發生變化。
3、Builder模式
(1)如何使用
builder模式結合了telescoping constructor模式的安全性和JavaBeans模式的可讀性。客戶端不直接調用所需對象,而是使用所有必須參數的構造方法(或靜態工廠方法)來獲取一個bulider對象;然後客戶端調用builder對象的和setter相似的方法來設置每個可選參數;最後,客戶端調用一個無參的builder方法來生成一個對象。
1 public static class Pizza { 2 public enum Topping {HAM, MUSHROOM, ONION} 3 final Set<Topping> toppings; 4 5 abstract static class Builder<T extends Builder<T>> { 6 EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class); 7 public T addTopping(Topping topping) { 8 toppings.add(topping); 9 return self(); 10 } 11 abstract Pizza build(); 12 13 protected abstract T self(); 14 } 15 16 Pizza(Builder<?> builder) { 17 toppings = builder.toppings.clone(); 18 } 19 } 20 21 public class MyPizza extends Pizza { 22 public enum Size{SMALL, MEDIUM, LARGE} 23 private final Size size; 24 25 public static class Builder extends Pizza.Builder<Builder> { 26 private final Size size; 27 28 public Builder(Size size) { 29 this.size = size; 30 } 31 32 @Override 33 public MyPizza build() { 34 return new MyPizza(this); 35 } 36 37 @Override 38 protected Builder self() { 39 return this; 40 } 41 } 42 43 private MyPizza(Builder builder) { 44 super(builder); 45 size = builder.size; 46 } 47 }
實例化方法:MyPizza pizza = new MyPizza.Builder(SMALL).addTopping(SAUAGE).build();
(2) 缺點
為了創建對象,必須先創建對象的builder,所以在關鍵的時候,性能會有稍許影響,而且builder模式的構建比較麻煩。
4、總結
當設計類的構造方法或靜態工廠的參數超過幾個時,Builder模式是一個不錯的選擇,特別是如果許多參數是可選的或相同類型的。
二、當構造方法參數過多時使用build模式