今天來介紹 Creational Patterns 當中的最後一個模式。
假設這裡有一個 Engineer
類別,他除了有個 name
屬性之外,還擁有 toolBox
,可以不斷加入新的工具,成為超級工具人。Engineer
和 ToolBox
的細節如下:
class Engineer {
name: string
toolBox: ToolBox
constructor(name: string) {
this.name = name
this.toolBox = new ToolBox()
}
}
class ToolBox {
tools: string[]
constructor(){
this.tools = []
}
addTools(tools: string[]) {
this.tools.push(...tools)
}
}
接著,我們就可以產生出一個 Engineer
實例如下
const a = new Engineer('foo')
a.toolBox.addTools(['a', 'b', 'c', 'd', 'e'])
a.name // 'foo'
a.toolBox.tools // ['a', 'b', 'c', 'd', 'e']
但這時候,如果有另外一位使用者,也想要擁有一個跟 a 一樣的Engineer
,有一樣的名字,和一樣的 tools,於是就用同樣的方式:先建立Engineer
實例,然後再加入 tools
const b = new Engineer('foo')
b.toolBox.addTools(['a', 'b', 'c', 'd', 'e'])
b.name // 'foo'
b.toolBox.tools // ['a', 'b', 'c', 'd', 'e']
就大功告成了!
今天要建立一個 b 看起來很簡單,但如果 a 本身是一個很複雜的物件,或者當中屬性的資料需要經過複雜的操作或運算才能獲得,那麼要建立一個跟 a 一模一樣的 b,就會變得非常麻煩。
那麼,有沒有一個可以讓我們直接複製 a 的方式呢?
為了讓我們可以快速地複製 Engineer
的實例,我們在 Engineer
當中加入了 clone
的方法。另一方面,也需要為 ToolBox
製作 clone
方法。細節如下:
class Engineer {
name: string
toolBox: ToolBox
constructor(name: string) {
this.name = name
this.toolBox = new ToolBox()
}
clone(): this {
const clone = Object.create(this)
clone.toolBox = this.toolBox.clone()
return clone
}
}
class ToolBox {
tools: string[]
constructor(){
this.tools = []
}
addTools(tools: string[]) {
this.tools.push(...tools)
}
clone(): ToolBox {
const clone = new ToolBox()
clone.addTools(this.tools)
return clone
}
}
接著,再讓我們重新建立一次 a
const a = new Engineer('foo')
a.toolBox.addTools(['a', 'b', 'c', 'd', 'e'])
然後,我們用 clone
方法來複製 a,立即得到一個跟 a 一模一樣的 b
const b = a.clone()
b.name // 'foo'
b.toolBox.tools // ['a', 'b', 'c', 'd', 'e']
雖然複製出來的時候是一模一樣,但實際上是兩個不同的、獨立的個體,所以我們可以分別為他們加入新的 tool,得到不同的結果
a.toolBox.addTools(['f'])
b.toolBox.addTools(['g'])
a.toolBox.tools // ['a', 'b', 'c', 'd', 'e', 'f']
b.toolBox.tools // ['a', 'b', 'c', 'd', 'e', 'g']
透過原型模式,讓我們能夠快速複製出同樣的物件,好處是我們透過複製,我們不需要再次經歷過該物件被建立的過程。
不過複製本身也帶有一些挑戰,如果一個物件裡面引用/指向了許多其他的物件,那麼我們就不能單純這些物件的複製 "reference" (shallow copy),而是要建立出獨立物件們 (deep copy)。所以越複雜的物件,要複製它就可能有更多的挑戰!