建造者模式又叫做生成器模式,是23種設(shè)計(jì)模式中的一種創(chuàng)建型模式。建造者模式,顧名思義,就是為創(chuàng)建對(duì)象而生的模式。
(一)定義
將一個(gè)復(fù)雜對(duì)象的構(gòu)建與表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
如果我們使用了建造者模式,客戶端只需要指定具體的建造類型,就可以創(chuàng)建出不同的對(duì)象,而每個(gè)對(duì)象的建造過程以及部件內(nèi)部的建造細(xì)節(jié)是對(duì)客戶端不可見的。也就是說,建造者模式很好的封裝了一個(gè)對(duì)象的構(gòu)建過程,并且把這個(gè)對(duì)象的每個(gè)組成部分進(jìn)行了封裝。從而可以到達(dá)另一個(gè)目的-復(fù)用??梢园l(fā)現(xiàn),建造者模式也是圍繞著面向?qū)ο蠓庋b這一特點(diǎn)進(jìn)行設(shè)計(jì)的。
(二)角色
Product:建造者模式中所要生產(chǎn)的產(chǎn)品,即要?jiǎng)?chuàng)建的復(fù)雜對(duì)象,這個(gè)復(fù)雜的對(duì)象至少由1個(gè)部分(部件)組成。
AbstractBuilder:定義了一些抽象接口,以表明產(chǎn)品對(duì)象的各個(gè)組成成分。這些接口實(shí)際上代表了要?jiǎng)?chuàng)建一個(gè)復(fù)雜的對(duì)象需要提供哪些部件(也就是這個(gè)復(fù)雜對(duì)象由哪幾部分組成),但并不涉及具體的部件的創(chuàng)建。每個(gè)部件具體的創(chuàng)建交由ConcreteBuilder來實(shí)現(xiàn)。同一個(gè)部件,不同的ConcreteBuilder可以提供不同的實(shí)現(xiàn)。
ConcreteBuilder:繼承自AbstractBuilder,內(nèi)部持有一個(gè)初始化好的Product實(shí)例,實(shí)現(xiàn)AbstractBuilder定義的接口,來為這個(gè)product實(shí)例(即我們需要構(gòu)建的復(fù)雜對(duì)象)的構(gòu)建提供部件。針對(duì)不同的業(yè)務(wù)場景,我們可以對(duì)每一個(gè)部件提供不一樣的實(shí)現(xiàn)。 然后把這些構(gòu)建好的部件(即組成復(fù)雜對(duì)象的部分)設(shè)置給product實(shí)例。
Director:調(diào)用具體建造者builder來創(chuàng)建復(fù)雜對(duì)象的各個(gè)部件,通常這個(gè)builder是作為一個(gè)參數(shù)從外界傳遞給director對(duì)象的,在director中不涉及產(chǎn)品的具體信息,director只負(fù)責(zé)操縱builder,來完成復(fù)雜對(duì)象各個(gè)部件的創(chuàng)建或者通過操作builder使復(fù)雜對(duì)象的各個(gè)部件按某種順序創(chuàng)建。
(三)UML類圖

(四)示例代碼
/// 產(chǎn)品類
class Product: NSObject {
var parts: [String] = [String]()
/// 添加部件
func add(_ part: String) {
parts.append(part)
}
/// 展示成品
func show() {
for i in 0 ..< parts.count {
print(parts[i])
}
}
}
/// 抽象建造者類
protocol IBuilder {
func buildPartA()
func buildPartB()
func buildPartC()
/// ...
func getResult() -> Product
}
class AbstractBuilder: NSObject, IBuilder {
private var product: Product = Product()
func buildPartA() {
}
func buildPartB() {
}
func buildPartC() {
}
func getResult() -> Product {
return product
}
}
/// 具體建造者類1
class ConcreteBuilder1: AbstractBuilder {
private var product: Product = Product()
override func buildPartA() {
product.add("partA1")
}
override func buildPartB() {
product.add("partB1")
}
override func buildPartC() {
product.add("partC1")
}
override func getResult() -> Product {
return product
}
}
/// 具體建造者類2
class ConcreteBuilder2: AbstractBuilder {
private var product: Product = Product()
override func buildPartA() {
product.add("partA2")
}
override func buildPartB() {
product.add("partB2")
}
override func buildPartC() {
product.add("partC2")
}
override func getResult() -> Product {
return product
}
}
/// 指揮者類
class Director: NSObject {
/// 使用客戶端提供的builder創(chuàng)建產(chǎn)品的各個(gè)部件
func construct(_ builder: AbstractBuilder) {
builder.buildPartA()
builder.buildPartB()
builder.buildPartC()
}
}
/// 客戶端調(diào)用
let director = Director()
let builder1 = ConcreteBuilder1()
director.construct(builder1)
let product = builder1.getResult()
product.show()
(五)優(yōu)點(diǎn)
- 實(shí)現(xiàn)了客戶端和產(chǎn)品對(duì)象創(chuàng)建過程的解耦。因?yàn)榻ㄔ煺吣J椒庋b了對(duì)象的構(gòu)建過程,使客戶端無需感知每個(gè)對(duì)象的構(gòu)建細(xì)節(jié)。所以更加符合面向?qū)ο蟮姆庋b特性,同時(shí)也有利于部件的復(fù)用。
- 易于擴(kuò)展,符合開放-封閉原則。因?yàn)椴煌慕ㄔ煺遙uilder負(fù)責(zé)生產(chǎn)不同的對(duì)象,不同的builder之間毫無影響,所以擴(kuò)展起來也相當(dāng)方便,無需修改原有的builder類,只需要增加一個(gè)具體的builder類即可,符合開放-封閉原則。
- 細(xì)節(jié)依賴抽象。將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程實(shí)現(xiàn)了分離(也就是解耦),產(chǎn)品本身不依賴于創(chuàng)建過程,使同樣的創(chuàng)建過程可以創(chuàng)建不同的對(duì)象(只是具體每個(gè)組件的實(shí)現(xiàn)不同)。
(六)缺點(diǎn)
- 因此使用場景受限。建造者模式一般只適用于創(chuàng)建的產(chǎn)品結(jié)構(gòu)相似的場景,即不同的產(chǎn)品具有相同的組成部件,只是組成部件的具體實(shí)現(xiàn)不同,所以使用場景有限。
- 建造者模式需要客戶端感知的比較多,客戶端需要知道director、concreteBuilder以及concreteProduct??梢越Y(jié)合簡單工廠模式來優(yōu)化。
(七)建造者模式 vs 工廠方法模式
- 都遵守開發(fā)-封閉原則。建造者模式是一個(gè)具體的builder構(gòu)建一種具體的產(chǎn)品。工廠方法模式是一個(gè)具體的factory生產(chǎn)一種具體的產(chǎn)品。這兩個(gè)模式中,構(gòu)建產(chǎn)品的類(concreteFactory和ConcreteBuilder)和產(chǎn)品類都是1 : 1的存在。
- 產(chǎn)品種類數(shù)量不同。建造者模式通常只有一個(gè)產(chǎn)品類,工廠方法模式通常有一個(gè)抽象產(chǎn)品類和多個(gè)具體產(chǎn)品類。
- 建造者模式不同的產(chǎn)品結(jié)構(gòu)相同(即組成部分相同),細(xì)節(jié)不同(具體組成部分的實(shí)現(xiàn)可能不同)。工廠方法模式不同的產(chǎn)品可能結(jié)構(gòu)和細(xì)節(jié)都不相同。
- 建造者模式更加側(cè)重于封裝對(duì)象各個(gè)組件的裝配順序。工廠方法模式側(cè)重的是封裝對(duì)象的創(chuàng)建過程。
(八)應(yīng)用場景
- 當(dāng)一個(gè)對(duì)象的構(gòu)建過程比較復(fù)雜時(shí)可以考慮使用建造者模式。
- 當(dāng)一個(gè)對(duì)象的構(gòu)造器存在多個(gè)參數(shù)(或者可以說由多個(gè)子對(duì)象組成)時(shí),可以考慮使用建造者模式。
- 建造者模式主要解決在軟件系統(tǒng)中,有時(shí)候面臨著"一個(gè)復(fù)雜對(duì)象"的創(chuàng)建工作,這個(gè)復(fù)雜對(duì)象通常由各個(gè)部分的子對(duì)象用一定的算法構(gòu)成;由于需求的變化,這個(gè)復(fù)雜對(duì)象的各個(gè)部分經(jīng)常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對(duì)穩(wěn)定。
用簡單工廠改造建造者模式
建造者模式中,客戶端不但要認(rèn)識(shí)director類,還要認(rèn)識(shí)builder類,結(jié)合簡單工廠模式,給director傳遞一個(gè)枚舉值,director內(nèi)部根據(jù)枚舉值創(chuàng)建對(duì)應(yīng)的builder,并讓builder構(gòu)造復(fù)雜對(duì)象的各個(gè)組成部分,最后把復(fù)雜對(duì)象返回。這樣的好處是,客戶端只感知director類和product類,缺點(diǎn)是director中的邏輯變得復(fù)雜,且不符合開放-封閉原則。
參考文章
java設(shè)計(jì)模式之建造者模式
建造者模式(Builder Pattern)- 最易懂的設(shè)計(jì)模式解析
文/VV木公子(簡書作者)
PS:如非特別說明,所有文章均為原創(chuàng)作品,著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并注明出處。