作為一個(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í)。
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)