把一個複雜物件的建構與樣貌分離,如此相同的建構過程可以產生不同樣貌的物件
先舉一個簡單的例子:組一台電腦!
現在我想要組一台全新的電腦,基本上需要主機板、硬碟以及cpu
public class Computer {
private String cpu;
private String mb;
private String hdd;
public Computer(String cpu, String mb, String hdd) {
this.cpu = cpu;
this.mb = mb;
this.hdd = hdd;
}
}
Computer myComputer = new Computer("apple", "apple", "apple");
這樣最基本的電腦就組好了。乍看之下,好像很簡單。但如果我要將配備升級呢?還需要顯示卡、鍵盤、滑鼠。
public class Computer {
private String cpu;
private String mb;
private String hdd;
private String vga;
private String keyboard;
private String mouse;
public Computer(String cpu, String mb, String hdd, String vga, String keyboard, String mouse) {
this.cpu = cpu;
this.mb = mb;
this.hdd = hdd;
this.vga = vga;
this.keyboard = keyboard;
this.mouse = mouse;
}
}
這樣改了之後其實也算符合需求。不過若是有別人想要組電腦,但已經有鍵盤滑鼠了,他不需要再買一樣的東西,那組電腦的方式勢必要再修改一下。這裏我們可以用overload的方式來解決這個問題。
public class Computer {
private String cpu;
private String mb;
private String hdd;
private String vga;
private String keyboard; // 選配
private String mouse; // 選配
public Computer(String cpu, String mb, String hdd, String vga, String keyboard, String mouse) {
this.cpu = cpu;
this.mb = mb;
this.hdd = hdd;
this.vga = vga;
this.keyboard = keyboard;
this.mouse = mouse;
}
public Computer(String cpu, String mb, String hdd, String vga, String mouse) {
this(cpu, mb, hdd, vga, null, mouse)
}
public Computer(String cpu, String mb, String hdd, String vga) {
this(cpu, mb, hdd, vga, null, null)
}
}
經過辛苦的更新後,終於可以依照第二第三種需求來組一台電腦了。不過可以發現,如果需求一直增加,那constructor勢必就會一直增加,這樣不單因為參數很多使用起來不方便,而且整體的可讀性及維運性都大大降低。
現在我們回到Builder Pattern的定義,把一個複雜物件的建構與樣貌分離,如此相同的建構過程可以產生不同樣貌的物件
。對於Computer來說,cpu這些東西就是元件。而Builder Pattern的目的就是要將這些元件做分離,不要全部擠在一起,然後在依照需求一一組裝起來,建立起各種需求的物件。
在使用Builder Pattern前,必須先來了解一下Builder Pattern內的四個成員
成員 | 功用 |
---|---|
Product | 最終要被建立出來的物件類別。 |
Builder | 用來定義建構物件過程中各必要步驟(方法)的介面。 |
ConcreteBuilder | 實作Builder介面,實際用來建構物件的類別。 |
Director | 負責指揮ConcreteBuilder該如何建構物件。 |
藉由Builder的介面先定義好組電腦所需要的方法及步驟。ConcreteBuilder實作Builder,並完成組裝的邏輯。最後經由Director呼叫ConcreteBuilder的實作的組裝步驟來將Product建立出來。
認識完每個成員的能力之後,就來把每個成員套進剛剛的範例試試。
class Computer {
private String cpu;
private String mb;
private String hdd;
private String vga;
private String keyboard; // 選配
private String mouse; // 選配
public Computer(String cpu, String mb, String hdd, String vga, String keyboard, String mouse) {
this.cpu = cpu;
this.mb = mb;
this.hdd = hdd;
this.vga = vga;
this.keyboard = keyboard;
this.mouse = mouse;
}
@Override
public String toString() { // 等等輸出時方便顯示
return "Computer : " + "\n" +
"cpu : " + this.cpu + "\n" +
"mb : " + this.mb + "\n" +
"hdd : " + this.hdd + "\n" +
"vga : " + this.vga +"\n" +
"keyboard : "+ this.keyboard +"\n" +
"mouse : "+ this.mouse;
}
}
abstract class ComputerBuilder {
protected Computer computer;
public abstract ComputerBuilder setCPU(String cpu);
public abstract ComputerBuilder setMB(String mb);
public abstract ComputerBuilder setHDD(String hdd);
public abstract ComputerBuilder setVGA(String vga);
public abstract ComputerBuilder setKeyboard(String keyboard);
public abstract ComputerBuilder setMouse(String mouse);
public abstract Computer build();
}
將組裝電腦所需要的方法先定義好讓ConcreteBuilder實作。
class ComputerFactory extends ComputerBuilder{
private String cpu;
private String mb;
private String hdd;
private String vga;
private String keyboard;
private String mouse;
public ComputerBuilder setCPU(String cpu){
this.cpu = cpu;
return this;
}
public ComputerBuilder setMB(String mb){
this.mb = mb;
return this;
}
public ComputerBuilder setHDD(String hdd){
this.hdd = hdd;
return this;
}
public ComputerBuilder setVGA(String vga){
this.vga = vga;
return this;
}
public ComputerBuilder setKeyboard(String keyboard){
this.keyboard = keyboard;
return this;
}
public ComputerBuilder setMouse(String mouse){
this.mouse = mouse;
return this;
}
public Computer build() {
return new Computer(cpu, mb, hdd, vga, keyboard, mouse);
}
}
將邏輯實作在Builder所定義好的方法,提供Director呼叫。
class ComputerStore {
private ComputerBuilder computerBuilder;
ComputerStore(ComputerBuilder computerBuilder){
this.computerBuilder = computerBuilder;
}
public Computer mixSpec(){
Computer computer = computerBuilder
.setCPU("intel")
.setMB("Apple")
.setHDD("acer")
.setVGA("msi")
.build();
return computer;
}
public Computer appleSpec(){
Computer computer = computerBuilder
.setCPU("Apple")
.setMB("Apple")
.setHDD("Apple")
.setVGA("Apple")
.build();
return computer;
}
}
依照需求呼叫ComputerFactory的方法,將Computer建立起來。
public class MyComputer {
public static void main(String[] args) {
ComputerFactory cf = new ComputerFactory();
ComputerStore cs = new ComputerStore(cf);
Computer c = cs.mixSpec();
System.out.println(c.toString());
}
}
output
Computer :
cpu : intel
mb : Apple
hdd : acer
vga : msi
keyboard : null
mouse : null
這就是Builder Pattern建立一台電腦的方法。可以發現,相較於前面的範例,Builder Pattern的彈性大了許多,整體維護起來也較為方便。但如果Product內的參數量不是太多,可能就無法感受這個Pattern方便的地方!
將元件做分離,依照需求一一組裝起來,建立不同需求的物件。
Product:最終要被建構物件的類別。
Builder:用來定義建構物件過程中各必要步驟(方法)的介面。
ConcreteBuilder:實作Builder介面,實際用來建構物件的類別。
Director:負責指揮ConcreteBuilder該如何建構物件。
優點
1. 方便使用者建立複雜的物件(不需要知道實現過程)。
2. 產品的建造和表示分離,實現瞭解耦。
3. 增加新的具體建造者無需修改原有類庫的程式碼,易於拓展,符合“開閉原則“。
缺點
1. 產品必須有共同點,限制了使用範圍。
2. 如內部變化複雜,會有很多的建造類,難以維護。
1. 當組裝物件的邏輯變複雜時。
2. 當未來客戶端需要其他客製化內容時。
聊 Java — Builder Pattern
白話 Design Pattern (四) Builder pattern