第十二章:構(gòu)建模式

作為一個(gè)打工仔,在外面辛苦了一年,李力終于要回家過年了。李力給他的小侄子買了兩套積木游戲玩具。小侄子玩的不亦樂乎,小侄子把四個(gè)輪子、一個(gè)車身、一個(gè)發(fā)動(dòng)機(jī)和一個(gè)方向盤拼裝成了一輛車。用一間客廳、兩個(gè)臥室、一間書房、一間廚房、一個(gè)花園和一堵圍墻搭建了一個(gè)莊園......

一、用代碼來模擬生活

from abc import ABCMeta, abstractmethod


class Toy(metaclass=ABCMeta):
    """玩具"""

    def __init__(self, name):
        self._name = name
        self.__components = []

    def getName(self):
        return self._name

    def addComponent(self, component, count=1, unit="個(gè)"):
        self.__components.append([component, count, unit])
        print("%s 增加了 %d %s%s" % (self._name, count, unit, component))

    @abstractmethod
    def feature(self):
        pass


class Car(Toy):
    """小車"""

    def feature(self):
        print("我是%s, 我可以快速奔跑......" % self._name)


class Manor(Toy):
    """莊園"""

    def feature(self):
        print("我是%s, 我可供觀賞。也可用來游玩!" % self._name)


class ToyBuilder:
    """玩具構(gòu)建者"""

    def buildCar(self):
        car = Car("迷你小車")
        print("正在構(gòu)建%s......" % car.getName())
        car.addComponent("輪子", 4)
        car.addComponent("車身", 1)
        car.addComponent("發(fā)動(dòng)機(jī)", 1)
        car.addComponent("方向盤")
        return car

    def buildManor(self):
        manor = Manor("淘淘小莊園")
        print("正在構(gòu)建 %s......" % manor.getName())
        manor.addComponent("客廳", 1, "間")
        manor.addComponent("臥室", 1, "間")
        manor.addComponent("書房", 1, "間")
        manor.addComponent("廚房", 1, "間")
        manor.addComponent("花園", 1, "個(gè)")
        manor.addComponent("圍墻", 1, "堵")
        return manor


if __name__ == "__main__":
    builder = ToyBuilder()
    car = builder.buildCar()
    car.feature()
    print("\n")
    mannor = builder.buildManor()
    mannor.feature()

用golang來演示:

package main

import "fmt"

type component struct {
    name  string
    count int
    unit  string
}

type ToyInterface interface {
    getName() string
    addComponent(c component)
    feature()
}

type Toy struct {
    name       string
    components []component
}

func (t Toy) getName() string {
    return t.name
}

func (t Toy) addComponent(c component) {
    t.components = append(t.components, c)
    fmt.Printf("%s 增加了 %d %s %s \n", t.name, c.count, c.unit, c.name)
}

func (t Toy) feature() {
    fmt.Println("我是玩具......")
}

type Car struct {
    Toy
}

func (c Car) feature() {
    fmt.Printf("我是%s,我可以快速奔跑......\n", c.name)
}

type Manor struct {
    Toy
}

func (m Manor) feature() {
    fmt.Printf("我是%s,我可供欣賞,也可用來游玩!", m.name)
}

type ToyBuilder struct {
}

func (t ToyBuilder) buildCar() ToyInterface {
    car := Car{Toy{
        name:       "迷你小車",
        components: make([]component, 0, 10),
    }}
    fmt.Printf("正在構(gòu)建%s......\n", car.getName())
    c1 := component{
        name:  "輪子",
        count: 4,
        unit:  "個(gè)",
    }
    car.addComponent(c1)
    c2 := component{
        name:  "車身",
        count: 1,
        unit:  "個(gè)",
    }
    car.addComponent(c2)
    c3 := component{
        name:  "發(fā)動(dòng)機(jī)",
        count: 1,
        unit:  "個(gè)",
    }
    car.addComponent(c3)
    c4 := component{
        name:  "方向盤",
        count: 1,
        unit:  "個(gè)",
    }
    car.addComponent(c4)
    return car
}

func (t ToyBuilder) buildManor() ToyInterface {
    manor := Manor{Toy{
        name:       "淘淘小莊園",
        components: make([]component, 5, 10),
    }}
    fmt.Printf("正在構(gòu)建%s......\n", manor.getName())
    c1 := component{
        name:  "客廳",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c1)
    c2 := component{
        name:  "臥室",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c2)
    c3 := component{
        name:  "書房",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c3)
    c4 := component{
        name:  "廚房",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c4)
    c5 := component{
        name:  "花園",
        count: 1,
        unit:  "個(gè)",
    }
    manor.addComponent(c5)
    c6 := component{
        name:  "圍墻",
        count: 1,
        unit:  "堵",
    }
    manor.addComponent(c6)
    return manor
}

func main() {
    builder := ToyBuilder{}
    car := builder.buildCar()
    car.feature()
    fmt.Println()
    mannor := builder.buildManor()
    mannor.feature()
}

二、設(shè)計(jì)思想

構(gòu)建顧名思義就是把各種部件通過一定的方式和流程構(gòu)造成一個(gè)成品的過程。在程序中,我們將這一過程稱為構(gòu)建模式(英文叫Builder Pattern,不同的書籍和資料翻譯各有不同,有的也叫建造者模式或生成器模式)。
構(gòu)建模式的核心思想是:將產(chǎn)品的創(chuàng)建過程與產(chǎn)品本身分離開來,使得創(chuàng)建過程更加清晰,能夠更加精確地控制復(fù)雜對(duì)象的創(chuàng)建過程,讓使用者可以用相同的創(chuàng)建過程創(chuàng)建不同的產(chǎn)品。

三、比較

3.1 與工廠模式的區(qū)別

工廠模式關(guān)注的是整個(gè)產(chǎn)品(整體對(duì)象)的生成,即成品的生成;而構(gòu)建模式關(guān)注的是產(chǎn)品的創(chuàng)建過程和細(xì)節(jié),一步一步地由各個(gè)子部件構(gòu)建為一個(gè)成品。
比如要?jiǎng)?chuàng)建一輛汽車,如果用工廠模式,直接就創(chuàng)建一輛有車身、輪胎、發(fā)動(dòng)機(jī)的能用的汽車。如果用構(gòu)建模式,則需要由車身、輪胎、發(fā)動(dòng)機(jī)一步一步地組裝成一輛汽車。

3.2與組合模式的區(qū)別

組合模式關(guān)注的是“整體-部分”的關(guān)系,也就是關(guān)注對(duì)象的內(nèi)部組成結(jié)構(gòu),那么它與構(gòu)建模式又有什么區(qū)別與聯(lián)系呢?
區(qū)別:組合模式關(guān)注的是對(duì)象內(nèi)部的組成結(jié)構(gòu),強(qiáng)調(diào)的是部分與整體的關(guān)系。構(gòu)建模式關(guān)注的是對(duì)象的創(chuàng)建過程,即由一個(gè)一個(gè)的子部件構(gòu)建一個(gè)成品的過程。
聯(lián)系:組合模式和構(gòu)建模式其實(shí)也經(jīng)常被一起使用。還是以組裝電腦為例,組合模式和構(gòu)建模式一起使用。
組裝電腦的時(shí)候,內(nèi)存卡(Memory Card)、硬盤(Hard Disk)、核心處理器(CPU)、電池(Battery)、風(fēng)扇(Fan)都是獨(dú)立的電子元件,而主板(Mainboard)和機(jī)箱(Computer Case)都是由子元件組成的。我們的ComputerBuilder就是構(gòu)建者,負(fù)責(zé)整個(gè)電腦的組裝過程:先把內(nèi)存卡、硬盤、“CPU組裝在主板上,再把主板、電池、風(fēng)扇組裝在機(jī)箱里,最后連接鼠標(biāo)、鍵盤、顯示器,就構(gòu)成了一臺(tái)完整的臺(tái)式電腦。

四、升級(jí)版

如果Toy不只有車(car)和莊園(Manor),還有飛機(jī)、坦克、摩天輪、過山車等,而且不只造一輛車和一個(gè)莊園,數(shù)量由用戶自己定。那么builder就會(huì)變得越來越臃腫,那么就需要進(jìn)行升級(jí)。
升級(jí)版構(gòu)建模式的類圖.png

Product是產(chǎn)品的抽象類(基類),ProductA和ProductB是具體的產(chǎn)品。Builder是抽象構(gòu)建類,ProductABuilder和ProductBBuilder是對(duì)應(yīng)產(chǎn)品的具體構(gòu)建類,而BuilderManager是構(gòu)建類的管理類(很多資料和書籍中叫它導(dǎo)演類(Director)),負(fù)責(zé)管理每一種產(chǎn)品的創(chuàng)建數(shù)量和創(chuàng)建順序。

package main

import "fmt"

// 組件
type component struct {
    name  string
    count int
    unit  string
}

type ToyInterface interface {
    getName() string
    addComponent(c component)
    feature()
}

// Toy 玩具
type Toy struct {
    name       string
    components []component
}

func (t Toy) getName() string {
    return t.name
}

func (t Toy) addComponent(c component) {
    t.components = append(t.components, c)
    fmt.Printf("%s 增加了 %d %s %s \n", t.name, c.count, c.unit, c.name)
}

func (t Toy) feature() {
    fmt.Println("我是玩具......")
}

// Car 小車
type Car struct {
    Toy
}

func (c Car) feature() {
    fmt.Printf("我是%s,我可以快速奔跑......\n", c.name)
}

// Manor 莊園
type Manor struct {
    Toy
}

func (m Manor) feature() {
    fmt.Printf("我是%s,我可供欣賞,也可用來游玩!", m.name)
}

type ToyBuilderInterface interface {
    buildProduct() ToyInterface
}

// ToyBuilder 玩具構(gòu)建者
type ToyBuilder struct {
}

func (t ToyBuilder) buildProduct() ToyInterface {
    return Toy{}
}

// CarBuilder 車的構(gòu)建類
type CarBuilder struct {
    ToyBuilder
}

func (t CarBuilder) buildProduct() ToyInterface {
    car := Car{Toy{
        name:       "迷你小車",
        components: make([]component, 0, 10),
    }}
    fmt.Printf("正在構(gòu)建%s......\n", car.getName())
    c1 := component{
        name:  "輪子",
        count: 4,
        unit:  "個(gè)",
    }
    car.addComponent(c1)
    c2 := component{
        name:  "車身",
        count: 1,
        unit:  "個(gè)",
    }
    car.addComponent(c2)
    c3 := component{
        name:  "發(fā)動(dòng)機(jī)",
        count: 1,
        unit:  "個(gè)",
    }
    car.addComponent(c3)
    c4 := component{
        name:  "方向盤",
        count: 1,
        unit:  "個(gè)",
    }
    car.addComponent(c4)
    return car
}

// ManorBuilder 莊園的構(gòu)建類
type ManorBuilder struct {
}

func (t ManorBuilder) buildProduct() ToyInterface {
    manor := Manor{Toy{
        name:       "淘淘小莊園",
        components: make([]component, 5, 10),
    }}
    fmt.Printf("正在構(gòu)建%s......\n", manor.getName())
    c1 := component{
        name:  "客廳",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c1)
    c2 := component{
        name:  "臥室",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c2)
    c3 := component{
        name:  "書房",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c3)
    c4 := component{
        name:  "廚房",
        count: 1,
        unit:  "間",
    }
    manor.addComponent(c4)
    c5 := component{
        name:  "花園",
        count: 1,
        unit:  "個(gè)",
    }
    manor.addComponent(c5)
    c6 := component{
        name:  "圍墻",
        count: 1,
        unit:  "堵",
    }
    manor.addComponent(c6)
    return manor
}

// BuilderMgr 構(gòu)建類的管理類
type BuilderMgr struct {
    carBuilder   CarBuilder
    manorBuilder ManorBuilder
}

func (b BuilderMgr) buildCar(num int) []ToyInterface {
    var products []ToyInterface
    for i := 0; i < num; i++ {
        car := b.carBuilder.buildProduct()
        products = append(products, car)

        fmt.Printf("建造完成第%d輛%s", i+1, car.getName())
    }
    return products
}

func (b BuilderMgr) buildManor(num int) []ToyInterface {
    var products []ToyInterface
    for i := 0; i < num; i++ {
        manor := b.manorBuilder.buildProduct()
        products = append(products, manor)

        fmt.Printf("建造完成第%d輛%s\n", i+1, manor.getName())
    }
    return products
}

func main() {
    builderMgr := BuilderMgr{carBuilder: CarBuilder{ToyBuilder{}}}
    builderMgr.buildCar(4)
    fmt.Println()
    builderMgr.buildManor(2)
}

運(yùn)行結(jié)果:

正在構(gòu)建迷你小車......
迷你小車 增加了 4 個(gè) 輪子 
迷你小車 增加了 1 個(gè) 車身 
迷你小車 增加了 1 個(gè) 發(fā)動(dòng)機(jī) 
迷你小車 增加了 1 個(gè) 方向盤 
建造完成第1輛迷你小車正在構(gòu)建迷你小車......
迷你小車 增加了 4 個(gè) 輪子 
迷你小車 增加了 1 個(gè) 車身 
迷你小車 增加了 1 個(gè) 發(fā)動(dòng)機(jī) 
迷你小車 增加了 1 個(gè) 方向盤 
建造完成第2輛迷你小車正在構(gòu)建迷你小車......
迷你小車 增加了 4 個(gè) 輪子 
迷你小車 增加了 1 個(gè) 車身 
迷你小車 增加了 1 個(gè) 發(fā)動(dòng)機(jī) 
迷你小車 增加了 1 個(gè) 方向盤 
建造完成第3輛迷你小車正在構(gòu)建迷你小車......
迷你小車 增加了 4 個(gè) 輪子 
迷你小車 增加了 1 個(gè) 車身 
迷你小車 增加了 1 個(gè) 發(fā)動(dòng)機(jī) 
迷你小車 增加了 1 個(gè) 方向盤 
建造完成第4輛迷你小車
正在構(gòu)建淘淘小莊園......
淘淘小莊園 增加了 1 間 客廳 
淘淘小莊園 增加了 1 間 臥室 
淘淘小莊園 增加了 1 間 書房 
淘淘小莊園 增加了 1 間 廚房 
淘淘小莊園 增加了 1 個(gè) 花園 
淘淘小莊園 增加了 1 堵 圍墻 
建造完成第1輛淘淘小莊園
正在構(gòu)建淘淘小莊園......
淘淘小莊園 增加了 1 間 客廳 
淘淘小莊園 增加了 1 間 臥室 
淘淘小莊園 增加了 1 間 書房 
淘淘小莊園 增加了 1 間 廚房 
淘淘小莊園 增加了 1 個(gè) 花園 
淘淘小莊園 增加了 1 堵 圍墻 
建造完成第2輛淘淘小莊園

4.1 模型說明

1.設(shè)計(jì)要點(diǎn)
構(gòu)建模式(升級(jí)版)中主要有三個(gè)角色,在設(shè)計(jì)構(gòu)建模式時(shí)要找到并區(qū)分這些角色。
(1)產(chǎn)品(Product):即你要構(gòu)建的對(duì)象。
(2)構(gòu)建者(Builder):構(gòu)建模式的核心類,負(fù)責(zé)產(chǎn)品的構(gòu)建過程。
(3)指揮者(BuilderManager):構(gòu)建的管理類,負(fù)責(zé)管理每一種產(chǎn)品的創(chuàng)建數(shù)量和創(chuàng)建順序。
2.構(gòu)建模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
(1)將產(chǎn)品(對(duì)象)的創(chuàng)建過程與產(chǎn)品(對(duì)象)本身分離開來,讓使用方(調(diào)用者)可以用相同的創(chuàng)建過程創(chuàng)建不同的產(chǎn)品(對(duì)象)。
(2)將對(duì)象的創(chuàng)建過程單獨(dú)分解出來,使得創(chuàng)建過程更加清晰,能夠更加精確地控制復(fù)雜對(duì)象的創(chuàng)建過程。
(3)針對(duì)升級(jí)版的構(gòu)建模式,每一個(gè)具體構(gòu)建者都相對(duì)獨(dú)立,而與其他的具體構(gòu)建者無關(guān),因此可以很方便地替換具體構(gòu)建者或增加新的具體構(gòu)建者。
缺點(diǎn):
(1)增加了很多創(chuàng)建類,如果產(chǎn)品的類型和種類比較多,將會(huì)增加很多類,“使整個(gè)系統(tǒng)變得更加龐雜。
(2)產(chǎn)品之間的結(jié)構(gòu)相差很大時(shí),構(gòu)建模式將很難適應(yīng)。

五、應(yīng)用場景

(1)產(chǎn)品(對(duì)象)的創(chuàng)建過程比較復(fù)雜,希望將產(chǎn)品的創(chuàng)建過程和它本身的功能分離開來。
(2)產(chǎn)品有很多種類,每個(gè)種類之間內(nèi)部結(jié)構(gòu)比較類似,但有很多差異;不同的創(chuàng)建順序或不同的組合方式,將創(chuàng)建不同的產(chǎn)品。
資料:
人人都懂設(shè)計(jì)模式:從生活中領(lǐng)悟設(shè)計(jì)模式:Python實(shí)現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容