遇到多个构造器参数时要考虑使用Builder
1、靜態(tài)工廠和構(gòu)造器有個共同的局限性:它們都不能很好地?cái)U(kuò)展到大量的可選參數(shù)。比如用一個類表示包裝食品外面顯示的營養(yǎng)成分標(biāo)簽。這些標(biāo)簽中有幾個域是必需的:每份的含量、每罐的含量以及每份的卡路里。還有超過2 0 個的可選域: 總脂肪量、飽和脂肪量、轉(zhuǎn)化脂肪、膽固醇、納,等等。大多數(shù)產(chǎn)品在某幾個可選域中都會有非零的值。
2、對于這樣的類,應(yīng)該用哪種構(gòu)造器或者靜態(tài)工廠來編寫呢?程序員一向習(xí)慣采用重疊構(gòu)造器( telescoping cons tructor )模式,在這種模式下,提供的第一個構(gòu)造器只有必要的參數(shù),第二個構(gòu)造器有一個可選參數(shù),第三個構(gòu)造器有兩個可選參數(shù),依此類推,最后一個構(gòu)造器包含所有可選的參數(shù)。下面有個示例,為了簡單起見,它只顯示四個可選域
第一種方法 構(gòu)造器
public class Computer {private String cup;private String keybord;private String screen;private String mouse;private String hardDisk;private String ram;private String colour;public Computer(String cup,String keybord) {this(cup,keybord,"");}public Computer(String cup,String keybord,String screen) {this(cup,keybord,screen,"");}public Computer(String cup,String keybord,String screen,String mouse) {this(cup,keybord,screen,mouse,"");}public Computer(String cup,String keybord,String screen,String mouse,String hardDisk) {this(cup,keybord,screen,mouse,hardDisk,"");}public Computer(String cup,String keybord,String screen,String mouse,String hardDisk,String ram) {this(cup,keybord,screen,mouse,hardDisk,ram,"");}public Computer(String cup,String keybord,String screen,String mouse,String hardDisk,String ram,String colour) {this.cup = cup;this.keybord = keybord;this.screen = screen;this.mouse = mouse;this.hardDisk = hardDisk;this.ram = ram;this.colour = colour;}public static void main(String[] args) {Computer computer = new Computer("I7","三星");} }這個構(gòu)造器調(diào)用通常需要許多你本不想設(shè)置的參數(shù),但還是不得不為它們傳遞值。在這個例子中,我們給colour傳遞了一個值為"" 。如果“僅僅”是這7個參數(shù),看起來還不算太糟糕,問題是隨著參數(shù)數(shù)目加,它很快就失去了控制
簡而言之,重疊構(gòu)造器模式可行,但是當(dāng)有許多參數(shù)的時候,客戶端代碼會很難縮寫,并且仍然較難以閱讀
遇到許多可選的構(gòu)造器參數(shù)的時候,還有第二種代替辦法,即JavaBeans 模式,在這種模式下,先調(diào)用一個無參構(gòu)造器來創(chuàng)建對象,然后再調(diào)用setter 方法來設(shè)置每個必要的參數(shù),以及每個相關(guān)的可選參數(shù):
第二種方法
public class Computer {private String cup;private String keybord;private String screen;private String mouse;private String hardDisk;private String ram;private String colour;public String getCup() {return cup;}public void setCup(String cup) {this.cup = cup;}public String getKeybord() {return keybord;}public void setKeybord(String keybord) {this.keybord = keybord;}public String getScreen() {return screen;}public void setScreen(String screen) {this.screen = screen;}public String getMouse() {return mouse;}public void setMouse(String mouse) {this.mouse = mouse;}public String getHardDisk() {return hardDisk;}public void setHardDisk(String hardDisk) {this.hardDisk = hardDisk;}public String getRam() {return ram;}public void setRam(String ram) {this.ram = ram;}public String getColour() {return colour;}public void setColour(String colour) {this.colour = colour;} }遺憾的是, JavaBeans 模式自身有著很嚴(yán)重的缺點(diǎn)。因?yàn)闃?gòu)造過程被分到了幾個調(diào)用中在構(gòu)造過程中Java Bean 可能處于不一致的狀態(tài)。類無法僅僅通過檢驗(yàn)構(gòu)造器參數(shù)的有效性來保證一致性。試圖使用處于不一致狀態(tài)的對象將會導(dǎo)致失敗,這種失敗與包含錯誤的代碼大相徑庭,因此調(diào)試起來十分困難。與此相關(guān)的另一點(diǎn)不足在于, Java Beans 模式使得把類做成不可變的可能性不復(fù)存在,這就需要程序員付出額外的努力來確保它的線程安全
我們現(xiàn)在來介紹第三種方式、它既能保證重疊構(gòu)造器模式那樣安全、也能像JavaBean那樣可讀。這就是建造之(Builder)模式。它不直接生成想要的對象,而是讓客戶端利用所有必要的參數(shù)調(diào)用構(gòu)造器(或者靜態(tài)工廠),得到一個builder 對象。然后客戶端在builder 對象上調(diào)用類似于setter 的方法,來設(shè)置每個相關(guān)的可選參數(shù)。最后客戶端調(diào)用無參的build 方法來生成通常是不可變的對象。
public class ProduceComputer implements Serializable {private String cup;private String keybord;private String screen;private String mouse;private String hardDisk;private String ram;private String colour;public static class Comuterbuilder implements Serializable{private String cup;private String keybord;private String screen;private String mouse;private String hardDisk;private String ram;private String colour;public Comuterbuilder(String cup,String keybord) {this.cup = cup;this.keybord = keybord;}public Comuterbuilder screen(String screen) {this.screen = screen;return this;}public Comuterbuilder mouse(String mouse) {this.mouse = mouse;return this;}public Comuterbuilder hardDisk(String hardDisk) {this.hardDisk = hardDisk;return this;}public Comuterbuilder colour(String colour) {this.colour = colour;return this;}public Comuterbuilder ram(String ram) {this.ram = ram;return this;}public ProduceComputer build() {return new ProduceComputer(this);}}private ProduceComputer(Comuterbuilder comuterbuilder) {cup = comuterbuilder.cup;keybord = comuterbuilder.keybord;screen = comuterbuilder.screen;mouse = comuterbuilder.mouse;hardDisk = comuterbuilder.hardDisk;ram = comuterbuilder.ram;colour = comuterbuilder.colour;}@Overridepublic String toString() {return super.toString();}public String getCup() {return cup;}public void setCup(String cup) {this.cup = cup;}public String getKeybord() {return keybord;}public void setKeybord(String keybord) {this.keybord = keybord;}public String getScreen() {return screen;}public void setScreen(String screen) {this.screen = screen;}public String getMouse() {return mouse;}public void setMouse(String mouse) {this.mouse = mouse;}public String getHardDisk() {return hardDisk;}public void setHardDisk(String hardDisk) {this.hardDisk = hardDisk;}public String getRam() {return ram;}public void setRam(String ram) {this.ram = ram;}public String getColour() {return colour;}public void setColour(String colour) {this.colour = colour;}public static void main(String[] args) {ProduceComputer produceComputer = new ProduceComputer.Comuterbuilder("I7", "三星").colour("yellow").hardDisk("1G").build();System.out.println(JSON.toJSONString(produceComputer));} }Builder 模式十分靈活,可以利用單個builder 構(gòu)建多個對象。build町的參數(shù)可以在調(diào)用build 方法來創(chuàng)建對象期間進(jìn)行調(diào)整,也可以隨著不同的對象而改變。builder 可以自動填充某些域,例如每次創(chuàng)建對象時自動增加序列號。
Builder 模式的確也有它自身的不足。為了創(chuàng)建對象,必須先創(chuàng)建它的構(gòu)建器。雖然創(chuàng)建這個構(gòu)建器的開銷在實(shí)踐中可能不那么明顯但是在某些十分注重性能的情況下,可能就成問題了。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è)計(jì)這種類時,
Builde 模式就是一種不錯的選擇, 特別是當(dāng)大多數(shù)參數(shù)都是可選或者類型相同的時候。與使用重疊構(gòu)造器模式相比,使用Bui lder 模式的客戶端代碼將更易于閱讀和編寫,構(gòu)建器也比JavaBeans 更加安全。
總結(jié)
以上是生活随笔為你收集整理的遇到多个构造器参数时要考虑使用Builder的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用静态工厂方法代替构造器
- 下一篇: 用私有构造器或者枚举类型强化Single