設計模式學習-使用go實現原型模式

Rick.lz發表於2021-11-08

原型模式

定義

如果物件的建立成本比較大,而同一個類的不同物件之間差別不大(大部分欄位都相同),在這種情況下,我們可以利用對已有物件(原型)進行復制(或者叫拷貝)的方式來建立新物件,以達到節省建立時間的目的。這種基於原型來建立物件的方式就叫作原型設計模式(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實現原型模式/

相關文章