相較於工廠系列的模式,著重在該產生
哪一個物件;我們也可能面臨到建構過程本身就複雜的狀況,這時
如何組裝出一個物件就可能是要解決的問題了,這裡要提到的是
建構者模式(Builder pattern)。
以學生餐廳的例子,如果將聚焦在點餐櫃台的動作,我們發現服務人員在接獲點餐後,分別有以下幾個動作:
炒飯櫃台:1.放上餐盤 、2. 放上免洗筷與鐵湯匙、3.放上炒飯、4.放上附湯。
麵點櫃台:1.放上餐盤 、2. 放上筷子與長湯匙、3.放上麵品、4.放上飲料 。
想想看,怎麼作比較好?
1.這次的重點在餐點的產生過程比較複雜,最好能夠將過程侷限在服務人員身上,點餐的學生只要享用該完成品,不需知道,也不需參予整個過程。
2.仔細分析,櫃台人員都有相同的四個動作,抽象的描述為:
1) 放上餐盤、2) 放上餐具、3) 放上餐點、4) 放上附湯飲料。
3.所以我們可以想像,整個模型有兩個部分:
1)執行四個動作:這部分兩個櫃台都一樣,見下圖的點餐服務
2) 對四個動作的各自表述:有一個抽象的餐點Builder與繼承它的炒飯Builder、麵食Builder
故,Builder Pattern的類別圖如下:
其中的Director就是負責執行各步驟並產出產品,Builder則確保了其繼承者都會有N個步驟。也有人將Director合併到Builder內,成為一種較簡化的變形。而這個模式中的幾個建構步驟,也可能與工廠系列的模式合作。
總之,Builder模式的重點有:
1.生產的流程複雜,需要被封裝。
2.每個builder都有相同的N個步驟或零件(步驟有先後順序,零件則無)。
前述的用法,產品較類似套餐,即各步驟或零件是固定的,選了A套餐,便能決定了每個部分是什麼。但實務上,如果到速食店點餐,雖然有分:主餐、副食、飲料三個部分,但可能個部分都是消費者當下選擇的(主餐可選雞塊/漢堡/雞堡、副食可選薯條/薯餅...),那就不可能在設計時期(Design Time)把所有的組合Builder都寫好,而需要代入多個參數,像下列:
餐點 A = new 餐點Builder(主食.雞塊, 副食.薯條, 飲料.coca).build();
那麼,如果建構時所需的參數非常多,要怎麼辦?下例是使用了Fluent Interface的技巧,讓新增客戶資料時,可以像以下這樣,簡潔易讀。
客戶 A = new 客戶Builder()
.Name("Rock")
.email("xxx@ooo.com")
.cellphone("09xx123456")
.build();
大致的寫法是:
public class 客戶
{
public 客戶(string ID, string Name, string email, string cellphone)
{
...
}
}
public class 客戶Builder
{
private 客戶 _Customer;
string _Name;
string _email;
string _cellphone;
public string Name(string Name)
{
_Name = Name;
return(this);
}
public string email(string email)
{
_email = email;
return(this);
}
public string cellphone (string cellphone)
{
_cellphone = cellphone;
return(this);
}
public 客戶 build()
{
_Customer = new 客戶(_Name, _email, _cellphone);
return(_Customer);
}
}
最後,如果有人因為看到Builder中包含了多個建置方法,而覺得會跟Abstract Factory搞混,這裡整理了Builder Pattern與Abstract Factory Pattern的不同:
1)
Builder著重在隱藏複雜的建置步驟,最後只傳回一個產品。
2)
Abstract Factory則是為了維護一系列產品的關聯,會產出某系列的多項產品。
3) Builder模式中,Client不需要認識各個零件的型態。(只要『吃』產出的餐點)
4) Abstract Factory中,Client認識各項的抽象型別或介面,並能使用它們。