【Golang设计模式】5.原型模式

5. 原型模式

原型模式,用原型实例指定创建对象的种类,并通过拷贝这些原型对象创建新的对象。

比如有一份简历,其中个人信息部分应该是相同的,所以我们可以以此基础作为原型,然后通过原型的Clone()方法生成新的实例,在新的实例中修改特有的信息,以达到我们的目的。此外,不同的实例进行修改应该是互不干扰的。具体操作如下:

在路径prototype\下新建文件prototype.go,包名为prototype

package prototype

// ...

简历中预计包含姓名、性别、学校、申请公司、申请职位等信息,这里我们用一个结构体来保存学校信息:

type School struct {
	Name    string
	Address string
	Level   string
}

// 创建School实例,返回对应指针
func NewSchool(name, address, level string) *School {
	return &School{
		Name:    name,
		Address: address,
		Level:   level,
	}
}

我们是通过原型的Clone()方法来拷贝一份新的实例的,所以为了不同实例之间能够互不干扰,我们应该使用深拷贝,School也要实现Clone()方法,这样原型对School()进行克隆的时候,就会调用School的Clone()方法。

// School也要实现Clone()方法
func (s *School) Clone() *School {
	return NewSchool(s.Name, s.Address, s.Level)
}

// 重写School的String()方法,便于输出信息
func (s *School) String() string {
	return "{" + s.Name + " " + s.Address + " " + s.Level + "}"
}

然后就是Resume结构体和对应的新建Resume实例方法:

在这里需要注意的是,Go中结构体是值类型而不是指针类型,创建新的复合结构体时,会把结构体的值复制一份过去,所以这里使用School指针。此外也可以节省一定空间。

type Resume struct {
	Name   string
	Gender string
	// 由于Go中结构体是值类型而不是指针类型,创建新的复合结构体时,会把值复制一份过去,所以这里使用School指针
	School        *School
	Apply4Company string
	Apply4Job     string
}

func NewResume(name, gender string, school *School, company, job string) *Resume {
	return &Resume{
		Name:          name,
		Gender:        gender,
		School:        school,
		Apply4Company: company,
		Apply4Job:     job,
	}
}

原型Clone()方法需要注意的点是,我们需要使用r.School.Clone()来把r.School(实际上是个指针)拷贝一份再放进去。

// 原型的Clone()方法
func (r *Resume) Clone() *Resume {
	// 注意这里的r.School.Clone()
	return NewResume(r.Name, r.Gender, r.School.Clone(), r.Apply4Company, r.Apply4Job)
}

在路径prototype的同级目录下新建main.go用于测试方法:

package main

import (
	"fmt"
	"github.com/loveshes/go-design-patterns/pattern/prototype-pattern/prototype"
)

func main() {
	ncu := prototype.NewSchool("南昌大学", "江西省南昌市", "211")
	proto := prototype.NewResume("王英俊", "男", ncu, "", "")

	// 简历一
	alibaba := proto.Clone()
	alibaba.Apply4Company = "Alibaba"
	alibaba.Apply4Job = "Java Web"
	fmt.Println("alibaba:", *alibaba)

	// 简历二
	bytedance := proto.Clone()
	// 修改复合结构体中的School.Level字段,看alibaba中的是否也会改变
	bytedance.School.Level = "双一流"
	bytedance.Apply4Company = "ByteDance"
	bytedance.Apply4Job = "Go"
	fmt.Println("修改School.Level后,alibaba:", *alibaba)
	fmt.Println("修改School.Level后,bytedance:", *bytedance)
}

输出为

alibaba: {王英俊 男 {南昌大学 江西省南昌市 211} Alibaba Java Web}
修改School.Level后,alibaba: {王英俊 男 {南昌大学 江西省南昌市 211} Alibaba Java Web}
修改School.Level后,bytedance: {王英俊 男 {南昌大学 江西省南昌市 双一流} ByteDance Go}

可以看到对简历二中School.Level的修改并没有影响到简历一。

此外可以把r.Clone()中的r.School.Clone()改成r.School,再看看有没有影响。

另外,由于Go中结构体本身就是值类型,把Resume结构体中的*School改成School,就算不使用r.School.Clone(),不同实例之间仍然互不干扰。

完整示例

posted @ 2020-04-22 22:00  爱Code的王饱饱  阅读(411)  评论(0编辑  收藏  举报