原型模式
定義
如果物件的建立成本比較大,而同一個類的不同物件之間差別不大(大部分欄位都相同),在這種情況下,我們可以利用對已有物件(原型)進行復制(或者叫拷貝)的方式來建立新物件,以達到節省建立時間的目的。這種基於原型來建立物件的方式就叫作原型設計模式(Prototype Design Pattern),簡稱原型模式。
原型模式是能基於拷貝來的,對於拷貝我們知道有兩種形式,深拷貝和淺拷貝
淺拷貝只複製指向某個物件的指標,而不復制物件本身,新舊物件還是共享同一塊記憶體。但深拷貝會另外創造一個一模一樣的物件,新物件跟原物件不共享記憶體,修改新物件不會改到原物件。
原型模式淺拷貝:
1、省記憶體,拷貝時間更快;
2、淺拷貝容易出現原始資料被修改的情況,一般不建議使用;
3、淺拷貝可以拷貝不可變物件;
原型模式深拷貝:
1、資料完全隔離;
2、不過資料量大的情況下,深拷貝比起淺拷貝來說,更加耗時,更加耗記憶體空間;
程式碼實現
// Cloneable 是原型物件需要實現的介面
type Cloneable interface {
Clone() Cloneable
}
type PrototypeManager struct {
prototypes map[string]Cloneable
}
func NewPrototypeManager() *PrototypeManager {
return &PrototypeManager{
prototypes: make(map[string]Cloneable),
}
}
func (p *PrototypeManager) Get(name string) Cloneable {
return p.prototypes[name].Clone()
}
func (p *PrototypeManager) Set(name string, prototype Cloneable) {
p.prototypes[name] = prototype
}
測試檔案
var (
deepCopyManager *PrototypeManager
shallowCopyManager *PrototypeManager
)
// 深拷貝實現Cloneable
type DeepCopy struct {
name string
}
func (t *DeepCopy) Clone() Cloneable {
tc := *t
return &tc
}
// 淺拷貝實現Cloneable
type ShallowCopy struct {
name string
}
func (t *ShallowCopy) Clone() Cloneable {
return t
}
func TestDeepCopyClone(t *testing.T) {
t1 := deepCopyManager.Get("dc")
t2 := t1.Clone()
// 深拷貝,指向的不是同一個變數的地址
if t1 == t2 {
t.Fatal("error! get clone not working")
}
t21 := t2.(*DeepCopy)
t21.name = "ShallowCopy-test"
t11 := t1.(*DeepCopy)
// 深拷貝name,不會影響到copy前的變數
if t11.name == t21.name {
t.Fatal("shallowCopy err")
}
}
func TestShallowCopyClone(t *testing.T) {
t1 := shallowCopyManager.Get("sc")
t2 := t1.Clone()
// 淺拷貝,變數地址的指向不變
if t1 != t2 {
t.Fatal("error! get clone not working")
}
t21 := t2.(*ShallowCopy)
t21.name = "ShallowCopy-test"
t11 := t1.(*ShallowCopy)
// 深拷貝name,copy之前的變數和copy之後的變數同時更改
if t11.name != t21.name {
t.Fatal("shallowCopy err")
}
}
func init() {
deepCopyManager = NewPrototypeManager()
dc := &DeepCopy{
name: "deepCopy",
}
deepCopyManager.Set("dc", dc)
shallowCopyManager = NewPrototypeManager()
sc := &ShallowCopy{
name: "shallowCopy",
}
shallowCopyManager.Set("sc", sc)
}
優點
1、使用原型模式建立物件比直接new一個物件在效能上要好的多,因為是直接進行的記憶體拷貝,比初始化效能上會好很多;
2、簡化物件的建立,對於建立物件就像我們在編輯文件時的複製貼上一樣簡單。
缺點
克隆包含迴圈引用的複雜物件可能會非常麻煩。
適用場景
1、在專案中,如果存在大量相同或相似物件的建立,如果用傳統的建構函式來建立物件,會比較複雜和耗費資源,用原型模式生產物件就很高效;
2、物件建立過程比較麻煩,但複製比較簡單的時候;
參考
【文中程式碼】https://github.com/boilingfrog/design-pattern-learning/tree/master/原型模式
【大話設計模式】https://book.douban.com/subject/2334288/
【極客時間】https://time.geekbang.org/column/intro/100039001
【原型模式】https://github.com/senghoo/golang-design-pattern
【原文地址】https://boilingfrog.github.io/2021/11/08/使用go實現原型模式/