• 【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(),不同实例之间仍然互不干扰。

    完整示例

  • 相关阅读:
    redis主从架构
    redis持久化
    git 首次push失败
    Java8 CompletableFuture
    Mac Item2自动远程连接服务器
    Java8 日期和时间类
    【LeetCode】31. 下一个排列
    【LeetCode】30. 串联所有单词的子串
    【LeetCode】29. 两数相除
    【LeetCode】28. 实现 strStr()
  • 原文地址:https://www.cnblogs.com/loveshes/p/12756323.html
Copyright © 2020-2023  润新知