Effective java item 2 note
Consider a builder when faced with mana constructor parameters.
在需要多參數的情況下,static factory跟一般的constructor不太適用,書上的例子提到像是你需要建立一個食品上的添加物跟成分的class,然後這些成分都是你的輸入,如果用一般的constructor你就可能會產生以下這樣的程式碼:
public class NutritionFacts { public String A; public String B; public String C; public String D; public NutritionFacts(String A) { this(A,””,””) } public NutritionFacts(String A,String B,String C) { this(A,B,C,”") } public NutritionFacts(String A,String B,String C,String D) { this.A = A; this.B = B; this.C = C; this.D = D; }}
這種寫法在更多參數的時候client會很難用這樣的class,要一邊對著哪個洞是哪個參數,如果是同樣的型態填錯可能也不知道,以我比較常用到的android studio這個IDE來說,他可以幫你檢查形態上的不同,也可以用一些快捷鍵來看看這個class需要哪些input參數.但如果都是同樣type的話還是得自己檢查有沒有輸入錯.
有一個替代方案是寫一個沒有參數的constructor,然後用setter function去對每個參數做設定,也就是JavaBeans模式.但這樣的寫法有個缺點,因為建構這個class的過程被分到每個setter去了,所以在建構過程中class可能處在不一致的狀態,也有可能拿到這個class object的時候再去改變既有的值,導致使用上可能會有不一樣的行為出現(可能就不會是thread safe了),這樣編寫程式的人就要額外在使用對象class的時候去做額外的檢查.
有種替代方案是我們還可以用builder模式去編寫這樣的class.可以保證建構過程的安全性,也可以有像JavaBeans的可讀性.
範例如下:
public class NutritionFacts {
private final String A;
private final String B;
private final String C;
private final String D;
public static class Builder {
// require
private final String A;
private final String B;
// optional
private String C = “”;
private String D = “”;
public Builder(String A, String B) {
this.A = A;
this.B = B;
}
public Builder setC(String C) {
this.C = C;
return this;
}
public Builder setD(String D) {
this.D = D;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
this.A = builder.A;
this.B = builder.B;
this.C = builder.C;
this.D = builder.D;
}
}
可以把檢查條件放在build裡面,如果有不吻合就丟出exception.也可以把檢查條件放在每個setter裡面,好處是不用在build的時候才發現有錯.然後跟constructor比較,builder可以有多個可變參數,constructor就比較不彈性一點.
你也可以利用泛型把builder做成一個abstract factory,像是:
public interface Builder<T> {
public T build();
}
書上有提到,通常帶有builder實例的方法通常會利用bounded wildcard type來限制constructor的類型參數,例如:
Tree buildTree(Builder<? Extends Node> nodeBuilder) { … }
還有有提到java中implement abstract factory的對象是class這點,如果用newInstance來替代build的話,會帶來某些問題,例如newInstance企圖使用class的無參數的constructor,如果class裡面沒有無參數的constructor的話,也不會有compile error,結果client端要去處理額外的exception等等,這邊我有點不太能理解,因為平常看到的abstract factory的寫法感覺比較像是https://www.tutorialspoint.com/design_pattern/abstract_factory_pattern.htm,裡面也沒有看到newInstance這類的method.有待再進一步的了解.
那麼builder模式有哪些缺點呢?
- 在建立object的時候你會再多用一個object來建立這個object.在很重視效能的情況下可能會有影響.
- 通常4個或以上的參數的時候用builder會比較感覺得到好讀感的好處,不然寫起來是有點冗長,不過可能要想一下你用builder的時機點,如果這個class是有很大的機會會被擴編的,例如sdk config的class,設定項可能會一直增多的情形下,就算這個class一開始只有2個參數,也建議用builder模式,因為等到參數一多要refactor的時候也是會花一些時間.
使用時機:
constructor有多個參數,然後尤其是大多數的參數是optional的時候.