创建型 原型模式

为什么需要原型模式

最主要应对的场景是对象创建成本比较大,同一个类的不同对象之间如果差别不大,例如大部分字段值可能相同,这种情况下可以通过复制已有对象的部分内容来应对创建新对象。

尤其是创建对象设计到rpc、网络、文件系统等速度较慢的io情况下,都可以考虑这种设计模型。

设计的时候就是考虑如何clone, 一般来说接口都必须带有Clone这个函数。

实现

注意Clone的实现应该尽量避免io,内存拷贝为主。

prototype.go

package prototype

import (
	"bytes"
	"fmt"
)

type Inode interface {
	Print(string) string
	Clone() Inode // 这个就是关键
}

type File struct {
	Name string
}

func (f *File) Print(sep string) string {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintln(sep + f.Name))
	return buf.String()
}

// Clone should be light weight using shallow copy
func (f *File) Clone() Inode {
	return &File{Name: f.Name + "_clone"}
}

type Dir struct {
	Nodes []Inode
	Name  string
}

func (d *Dir) Print(sep string) string {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintln(sep + d.Name))
	for _, i := range d.Nodes {
		buf.WriteString(i.Print(sep + sep))
	}
	return buf.String()
}

// Clone should be light weight using shallow copy
func (d *Dir) Clone() Inode {
	cloneDir := &Dir{Name: d.Name + "_clone"}
	var tempNodes []Inode
	for _, i := range d.Nodes {
		tempNodes = append(tempNodes, i.Clone())
	}
	cloneDir.Nodes = tempNodes
	return cloneDir
}

prototype_test.go

package prototype_test

import (
	"prototype"
	"testing"
)

var expectPrint1 string = `  Dir1
    File1
    File2
    File3
`

var expectPrint2 string = `  Dir1_clone
    File1_clone
    File2_clone
    File3_clone
`

func TestPrototype(t *testing.T) {
	file1 := &prototype.File{Name: "File1"}
	file2 := &prototype.File{Name: "File2"}
	file3 := &prototype.File{Name: "File3"}
	dir1 := &prototype.Dir{Name: "Dir1", Nodes: []prototype.Inode{file1, file2, file3}}
	if dir1.Print("  ") != expectPrint1 {
		t.Fatal("Got:\n" + dir1.Print("  ") + "But expect:" + expectPrint1 + "#")
	}

	cloneDir := dir1.Clone()
	if cloneDir.Print("  ") != expectPrint2 {
		t.Fatal("Got:\n" + cloneDir.Print("  ") + "But expect:" + expectPrint2 + "#")
	}
}

执行: go test prototype -v

Previous
Next