Effective java item 2 note

Consider a builder when faced with mana constructor parameters.

Arwii
mycodingjourney
5 min readApr 15, 2018

--

在需要多參數的情況下,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模式有哪些缺點呢?

  1. 在建立object的時候你會再多用一個object來建立這個object.在很重視效能的情況下可能會有影響.
  2. 通常4個或以上的參數的時候用builder會比較感覺得到好讀感的好處,不然寫起來是有點冗長,不過可能要想一下你用builder的時機點,如果這個class是有很大的機會會被擴編的,例如sdk config的class,設定項可能會一直增多的情形下,就算這個class一開始只有2個參數,也建議用builder模式,因為等到參數一多要refactor的時候也是會花一些時間.

使用時機:

constructor有多個參數,然後尤其是大多數的參數是optional的時候.

--

--

Arwii
mycodingjourney

Try not to become a man of success, but rather try to become a man of value — A. Einstein