意圖
用原型實(shí)例指定創(chuàng)建對(duì)象的種類请祖,并且通過拷貝這些原型創(chuàng)建新的對(duì)象订歪。
動(dòng)機(jī)
我們希望我們的框架類和應(yīng)用類進(jìn)行解藕脖祈,但又必須知道如何創(chuàng)建應(yīng)用類的對(duì)象。(很抽象=刷晋。=)
使用性
- 當(dāng)要實(shí)例化的類是在運(yùn)行時(shí)刻指定是盖高,例如,通過動(dòng)態(tài)加載
- 為了避免創(chuàng)建一個(gè)與產(chǎn)品類層次平行的工廠類層次
- 當(dāng)一個(gè)類的實(shí)例只能有幾個(gè)不同狀態(tài)組合中的一種眼虱。
結(jié)構(gòu)
## 參與者
- Prototype
—— 聲明一個(gè)克隆自身的接口喻奥。
- ConcretePrototype
—— 實(shí)現(xiàn)一個(gè)克隆自身的操作。
- Client
—— 讓一個(gè)原型克隆自身從而創(chuàng)建一個(gè)新的對(duì)象捏悬。
協(xié)助
- 客戶請(qǐng)求一個(gè)原型克隆自身撞蚕。
效果
Prototype 有許多和 Abstract Factory 和 Builder 一樣的效果: 它對(duì)客戶隱藏了具體的產(chǎn)品類,因此減少了客戶知道的名字的數(shù)目过牙。
- 運(yùn)行時(shí)刻增加和刪除產(chǎn)品
- 改變值以指定新對(duì)象
- 改變結(jié)構(gòu)以指定新對(duì)象
- 減少子類的構(gòu)造
- 用類動(dòng)態(tài)配置應(yīng)用
實(shí)現(xiàn)原型是甥厦,要考慮的問題
- 使用一個(gè)原型管理器
當(dāng)一個(gè)系統(tǒng)的原型數(shù)目不固定時(shí)(也就是說,它們可以動(dòng)態(tài)創(chuàng)建和銷毀)寇钉,要保持一個(gè)可用原型的注冊(cè)表刀疙。
原型管理器是一個(gè)關(guān)聯(lián)存儲(chǔ)器,它返回一個(gè)與給定關(guān)鍵字匹配的原型扫倡。
- 實(shí)現(xiàn)克隆操作
- 初始化克隆對(duì)象
維基百科定義
寫下了這么多筆記谦秧,這么多概念還是有點(diǎn)蒙圈 =。= 撵溃,于是又到維基百科搜了一下疚鲤。
維基百科給出的定義:
原型模式是創(chuàng)建型模式的一種,其特點(diǎn)在于通過“復(fù)制”一個(gè)已經(jīng)存在的實(shí)例來返回新的實(shí)例,而不是新建實(shí)例。被復(fù)制的實(shí)例就是我們所稱的“原型”缘挑,這個(gè)原型是可定制的集歇。
原型模式多用于創(chuàng)建復(fù)雜的或者耗時(shí)的實(shí)例,因?yàn)檫@種情況下卖哎,復(fù)制一個(gè)已經(jīng)存在的實(shí)例使程序運(yùn)行更高效鬼悠;或者創(chuàng)建值相等删性,只是命名不一樣的同類數(shù)據(jù)
這就理解了!焕窝!其實(shí)就是 OC 中的 copy蹬挺,C++中的拷貝構(gòu)造函數(shù) =。= 它掂。
代碼示例
我們來看看在 Swift 這么實(shí)現(xiàn)吧巴帮。其實(shí) Swift 結(jié)構(gòu)體本身就是值語義的 = = 。所以我用 Class 虐秋,根據(jù)書上的例子來個(gè) Swift 版好了榕茧。
下面是我們 Swift 版本的 MazePrototypeFactory
。使用泛型可以讓我們的 MazePrototypeFactory
適用于不同類型的 Door客给,Wall用押,Room 。 這里新的構(gòu)造器只初始化它的原型靶剑。
class MazePrototypeFactory<W, D, R>: MazeFactory where W: WallType, D: DoorType, R: RoomType {
var prototypeMaze: Maze
var prototypeDoor: D
var prototypeWall: W
var prototypeRoom: R
func makeRoom(_ n: Int) -> RoomType {
return R(no: n)
}
func makeMaze() -> Maze {
return Maze()
}
init(m: Maze, d: D, w: W, r: R) {
prototypeMaze = m
prototypeDoor = d
prototypeWall = w
prototypeRoom = r
}
}
我們以克隆原型的方式定義蜻拨,makeWall
和 makeDoor
。
extension MazePrototypeFactory {
func makeWall() -> WallType {
return prototypeWall.clone()
}
func makeDoor(r1: RoomType, r2: RoomType) -> DoorType {
let door = prototypeDoor.clone()
door.initialize(r1: r1, r2: r2)
return door
}
}
我們只需要使用基本的原型進(jìn)行初始化,就可以由 MazePrototypeFactory 來創(chuàng)建一個(gè)原型的或缺省得迷宮桩引。
let simpleMazeFactory = MazePrototypeFactory(m: Maze(), d: Door(), w: Wall(), r: Room())
let game = MazeGame.createMaze(mazeFactory: simpleMazeFactory)
print("\(game)")
打印結(jié)果:
===========================
Maze room:
room_2 Room
north is Optional(Wall)
south is Optional(Wall)
east is Optional(Wall)
west is Optional(Door)
room_1 Room
north is Optional(Wall)
south is Optional(Wall)
east is Optional(Door)
west is Optional(Wall)
===========================
和之前的例子一樣缎讼,我們又需要定義一個(gè) Bombed 的迷宮。這次我們就不需要去定義一個(gè) Bombed 的抽象工廠類了坑匠。我們以 BombedWall
和 RoomWithABomb
作為原型就可以構(gòu)造出一個(gè) Bombed 的迷宮血崭。
let bombMazeFactory = MazePrototypeFactory(m: Maze(), d: Door(), w: BombedWall(), r: RoomWithABomb())
let bombedGame = MazeGame.createMaze(mazeFactory: bombMazeFactory)
print("\(bombedGame)")
打印結(jié)果:
===========================
Maze room:
room_2 RoomWithABomb Bombe is false
north is Optional(BombedWall Bombe is false)
south is Optional(BombedWall Bombe is false)
east is Optional(BombedWall Bombe is false)
west is Optional(Door)
room_1 RoomWithABomb Bombe is false
north is Optional(BombedWall Bombe is false)
south is Optional(BombedWall Bombe is false)
east is Optional(Door)
west is Optional(BombedWall Bombe is false)
===========================
總結(jié)
Prototype 一般可以和 Abstract Factory 一起使用,大量使用 Composite 和 Decorator 也可以 Prototype 獲得收益厘灼。
歡迎討論夹纫、批評(píng)、指錯(cuò)手幢。