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