在了解原型设计模式之前我们需要新知道Golang的深拷贝与浅拷贝之间的区别。
数据结构:
//速度速值
type Speed int
//风扇转速
type FanSpeed struct {
Speed Speed
}
//售价
type Money struct {
Length float64
}
//内存数量以及大小
type Memory struct {
Count int
MemorySize []int
}
//电脑信息
type Computer struct {
SystemName string //系统名字
UseNumber int //使用次数
Memory Memory //存储
Fan map[string]FanSpeed //风扇
Money Money //售价
}
浅拷贝:
接触过 Java或者C#的同学应该知道浅拷贝对于值类型的话是完全拷贝一份,而对于引用类型是拷贝其地址。也就是拷贝的对象修改引用类型的变量同样会影响到源对象。
这里Golang同理,在上述测试类型中涉及到 Slice 和 Map的修改则会互相影响。
测试1:
func ComputerStart1() {
Pc1 := Computer{
SystemName: "Windows",
UseNumber: 1000,
Memory: Memory{Count: 4, MemorySize: []int{32, 32, 32, 32}},
Fan: map[string]FanSpeed{"left": {2500}, "right": {2000}},
Money: Money{123.45},
}
//浅拷贝
Pc2:=Pc1
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
//修改切片内容以及map信息影响Pc1
Pc2.SystemName ="MacOs"
Pc2.UseNumber =100
Pc2.Memory.Count =2
Pc2.Memory.MemorySize[0]=8
Pc2.Memory.MemorySize[1]=8
Pc2.Memory.MemorySize[2]=0
Pc2.Memory.MemorySize[3]=0
Pc2.Fan["left"]=FanSpeed{2000}
Pc2.Fan["right"]=FanSpeed{1500}
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
}
输入信息:
PcInfo Pc1:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}, Pc2:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}
PcInfo Pc1:{Windows 1000 {4 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}, Pc2:{MacOs 100 {2 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}
对于PC2的修改影响到了PC1的Slice 和 Map
测试2:
func ComputerStart2() {
Pc1 := Computer{
SystemName: "Windows",
UseNumber: 1000,
Memory: Memory{Count: 4, MemorySize: []int{32, 32, 32, 32}},
Fan: map[string]FanSpeed{"left": {2500}, "right": {2000}},
Money: Money{123.45},
}
//浅拷贝
Pc2:=Pc1
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
ModifyCat(Pc2)
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
}
func ModifyCat(pc Computer) {
fmt.Printf("PcInfo Pc1:%v\n", pc)
pc.SystemName ="MacOs"
pc.UseNumber =100
pc.Memory.Count =2
pc.Memory.MemorySize[0]=8
pc.Memory.MemorySize[1]=8
pc.Memory.MemorySize[2]=0
pc.Memory.MemorySize[3]=0
pc.Fan["left"]=FanSpeed{2000}
pc.Fan["right"]=FanSpeed{1500}
fmt.Printf("PcInfo Pc1:%v\n", pc)
}
输入:
PcInfo Pc1:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}, Pc2:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}
PcInfo Pc1:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}
PcInfo Pc1:{MacOs 100 {2 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}
PcInfo Pc1:{Windows 1000 {4 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}, Pc2:{Windows 1000 {4 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}
这里在方法中修改PC2同样影响到了 PC1以及PC2,是因为在Golang中方法中传递的参数同样被拷贝了一份,他们修改的Slice 和 Map都是同一份地址。
那么对于浅拷贝来说如何避免这种情况的发生呢?
测试3:
func ComputerStart2() {
Pc1 := Computer{
SystemName: "Windows",
UseNumber: 1000,
Memory: Memory{Count: 4, MemorySize: []int{32, 32, 32, 32}},
Fan: map[string]FanSpeed{"left": {2500}, "right": {2000}},
Money: Money{123.45},
}
//浅拷贝
Pc2:=Pc1
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
//切片以及map新空间互不影响
Pc2.SystemName ="MacOs"
Pc2.UseNumber =100
Pc2.Memory =Memory{Count: 2, MemorySize: []int{8, 8}}
Pc2.Fan =map[string]FanSpeed{"left": {2000}, "right": {1500}}
Pc2.Money =Money{1000.45}
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
}
输出:
PcInfo Pc1:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}, Pc2:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}
PcInfo Pc1:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}, Pc2:{MacOs 100 {2 [8 8]} map[left:{2000} right:{1500}] {1000.45}}
既然只有Slice 和 Map会受到影响我们这里重新给定地址重新生成一个Slice和Map就可以不受影响。
深拷贝
对于深拷贝就比较好了解了,任何对象都会被完完整整的拷贝一份,拷贝对象与被拷贝对象不存在如何联系,也就不会互相影响。如果你需要拷贝的对象中没有引用类型,那么对于Golang而言使用浅拷贝就可以了。
基于序列化和反序列化来实现对象的深度拷贝:
func deepCopy(dst, src interface{}) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
return err
}
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}
需要深拷贝的变量必须首字母大写才可以被拷贝
测试1:
func ComputerStart4() {
Pc1 := &Computer{
SystemName: "Windows",
UseNumber: 1000,
Memory: Memory{Count: 4, MemorySize: []int{32, 32, 32, 32}},
Fan: map[string]FanSpeed{"left": {2500}, "right": {2000}},
Money: Money{123.45},
}
//深拷贝
Pc2:= new(Computer)
if err:= deepCopy(Pc2,Pc1);err!=nil{
panic(err.Error())
}
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
ModifyCat1(*Pc2)
fmt.Printf("PcInfo Pc1:%v, Pc2:%v\n", Pc1, Pc2)
}
输出:
PcInfo Pc1:&{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}, Pc2:&{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}
PcInfo Pc1:{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}
PcInfo Pc1:{MacOs 100 {2 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}
PcInfo Pc1:&{Windows 1000 {4 [32 32 32 32]} map[left:{2500} right:{2000}] {123.45}}, Pc2:&{Windows 1000 {4 [8 8 0 0]} map[left:{2000} right:{1500}] {123.45}}
可以看到PC2经过浅拷贝(参数传递)在修改Slice和Map受到影响的也只有PC2和PC2的浅拷贝对象。对于PC1没有任何影响。