Prototype Pattern,創建物件不再從頭開始浪費時間
Prototype Pattern,創建物件不再從頭開始浪費時間

Prototype Pattern,創建物件不再從頭開始浪費時間

Tags
Golang
Hey! Go Design Patterns
ithome 2021 ironman
Date
Sep 19, 2021

什麼是 Prototype Pattern?

將物件以拷貝的方式建立
如果某類物件在初始化時,會消耗大量的成本,例如:「對 DB 做 query、讀取 json file、讀取使用者滑鼠的軌跡」,那在建立完此物件後,又須建立此此類相似的物件時,我們會希望用拷貝(copy)的,以避免初始化的成本。
而拷貝這件事,又有分深拷貝(deep copy)淺拷貝(shallow copy),如果是有 javascript 經驗的開發者一定很熟悉這兩個詞,在 golang 他們主要是:
  • 深拷貝(deep copy):當拷貝時是拷貝數據本身,所以對拷貝的數據修改,不會影響到原本的數據,Int、Float、String、Bool、Struct、Array 都採用這類複製
  • 淺拷貝(shallow copy):當拷貝時是拷貝數據指標,所以對拷貝的數據修改,會影響到原本的數據,Slice、Map 都採用這類複製
使用淺拷貝通常是為了更節省記憶體,但如果複製出來的數據有要完全獨立的需求得採用深拷貝,
小節一下 Prototype Pattern 的優缺點:
優點:
  • 不需要初始化的成本
缺點:
  • 需要注意深淺拷貝的差異,不然有可能會修改到原始的數據

問題情境

Sony 已經做出了 PS5 原型機,在這個過程中有經歷「建模、分析硬體數據、效能測試」等動作,但現在要量產 PS5,並不需要這些浪費時間的動作,也不需要使用原型的組件來量產。

解決方式

相關的 code 在Github - go-design-patterns
package main import ( "fmt" "time" ) type PS5 struct { Version string CPU []string GPU map[string]string } func (_ PS5) Modeling() { time.Sleep(time.Second) // 模擬耗時 } func (_ PS5) LoadTest() { time.Sleep(time.Second) // 模擬耗時 } func (_ PS5) Analysis() { time.Sleep(time.Second) // 模擬耗時 } func (p *PS5) Clone() *PS5 { // 將基本物件拷貝 ps5 := PS5{} ps5 = *p // 由於Slice是淺拷貝,所以必須透過copy方法來把深層元素拷貝 cpu := make([]string, len(p.CPU)) copy(cpu, p.CPU) ps5.CPU = cpu // 由於Map是淺拷貝,所以需把元素一個一個拿出來拷貝 gpu := make(map[string]string) for k, v := range p.GPU { gpu[k] = v } ps5.GPU = gpu return &ps5 } func CreatePrototypePS5() *PS5 { prototypePS5 := PS5{ Version: "Prototype", CPU: []string{"原型CPU"}, GPU: make(map[string]string), } prototypePS5.GPU["GPU"] = "原型GPU" prototypePS5.Modeling() prototypePS5.LoadTest() prototypePS5.Analysis() return &prototypePS5 } func main() { prototypePS5 := CreatePrototypePS5() ps5 := prototypePS5.Clone() ps5.Version = "Version-1" ps5.CPU[0] = "量產CPU" ps5.GPU["GPU"] = "量產GPU2" fmt.Println(prototypePS5) fmt.Println(ps5) }
範例中PS5{}.CPUPS5{}.GPU分別是 Slice 與 Map,由於都是淺拷貝的數據,所以需要手動以copy()與 for 迴圈來將物件元素一個一個複製,而PS5{}.Version就不需要手動複製,因為是深拷貝的數據形態