创建型 - 4. 原型模式

87 阅读2分钟

1. 原型模式的原理与应用

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式

其他场景,例如对象包含了一些私有属性、或者对象本身对外暴露的方式就是一个接口,要创建一个新的对象,只能使用原型模式进行复制。

2. 原型模式的实现方式

2.1 深拷贝与浅拷贝区别

浅拷贝得到的对象跟原始对象共享数据,而深拷贝得到的是一份完完全全独立的对象。

如果要拷贝的对象是不可变对象,浅拷贝共享不可变对象是没问题的,但对于可变对象来说,浅拷贝得到的对象和原始对象会共享部分数据,就有可能出现数据被修改的风险。

2.2 如何实现深拷贝

  1. 递归拷贝对象、对象的引用对象以及引用对象的引用对象……直到要拷贝的对象只包含基本数据类型数据,没有引用对象为止。
  2. 先将对象序列化,然后再反序列化成新的对象。

2.3 原型模式的代码实现

type INode interface {
   Print(indentation string)
   Clone() INode
}

type File struct {
   name string
}

func (f *File) Print(indentation string) {
   fmt.Println(indentation + f.name)
}

func (f *File) Clone() INode {
   return &File{name: f.name + "_clone"}
}

type Folder struct {
   name     string
   children []INode
}

func (f *Folder) Print(indentation string) {
   fmt.Println(indentation + f.name)
   newIndentation := indentation + indentation
   for _, child := range f.children {
      child.Print(newIndentation)
   }
}

func (f *Folder) Clone() INode {
   newFolder := &Folder{name: f.name + "_clone"}
   var children []INode
   for _, child := range f.children {
      children = append(children, child.Clone())
   }
   newFolder.children = children
   return newFolder
}

// 客户端使用
func TestClone(t *testing.T) {
   file1 := &File{name: "file1"}
   file2 := &File{name: "file2"}
   file3 := &File{name: "file3"}

   folder1 := &Folder{
      name:     "Folder1",
      children: []INode{file1, file2, file3},
   }

   folder2 := &Folder{
      name:     "Folder2",
      children: []INode{folder1, file2, file3},
   }
   folder2.Print("  ")

   cloneFolder := folder2.Clone()

   cloneFolder.Print("  ")
}

3. 创建型设计模式总结

创建型模式主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。

  • 单例模式用来创建全局唯一的对象。
  • 工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。
  • 建造者模式是用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。
  • 原型模式针对创建成本比较大的对象,利用对已有对象进行复制的方式进行创建,以达到节省创建时间的目的。