45

设计模式-原型模式

 4 years ago
source link: https://www.tuicool.com/articles/Uf6ne2r
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

原型模式(Prototype Pattern):使用原型实例创建指定创建对象的种类,并通过拷贝这些原型创建新对象。这个模式很好理解,就是 ctrl+c,ctrl+v 后做一些小修改。

这里面涉及一个知识点就是深拷贝和浅拷贝的问题,但我相信任何python开发人员都知道 copy()deepcopy() 的区别,这里就不多说了(有兴趣的可以去看python中这2个函数的实现)。

个人理解当需要多个类对象时,如果要进行很多复杂的、消耗时间的初始化操作,而这些对象之间又仅有少量不同时,可以考虑使用原型模式。

package main

import "fmt"

type Cat struct {
	name string
	age  int
}

type Dog struct {
	name string
	age  int
	food []string
}

func main() {
	a := Cat{name: "tom", age: 3}
	b := a
	fmt.Println(a == b) // true a和b的值相同
	fmt.Println(&b == &a) // false  a和b的内存地址不同
	b.name = "roy"
	fmt.Printf("a:%v \nb:%v \n",a,b)
	fmt.Println(a == b) // false a和b的值不同

	c := Dog{name: "cola",age:1,food:[]string{"beef","chicken"}}
	d := c
	d.food[0] = "poke" // 修改d的food会影响到c
	fmt.Printf("struct c.food:%p\nstruct d.food:%p\n",&c.food,&d.food)
	fmt.Printf("struct c.food[0]:%p\nstruct d.food[0]:%p\n",&c.food[0],&d.food[0]) // 从这里可以看出最终内存地址是一样的

	e := c
	e.food = append(e.food,"poke") // append方法返回了一个新对象
	fmt.Printf("struct e.food:%p\n",&e.food)
	fmt.Printf("struct e.food[0]:%p\n",&e.food[0]) // 这里可以看出e的第一个元素已经和c、d不一样了

	fmt.Printf("c:%v",c)
	fmt.Printf("d:%v",d)
	fmt.Printf("e:%v",e)
}

Cat 这个类中不包含引用类型,所以直接将a赋值给b后,对b的修改不会影响a。但是 Dog 类中包含了引用类型,赋值后修改c、d任何一个的food值都会影响另一个。但是如果通过 append() 方法则是返回了一个新的slice,修改 e.food 就不会影响c和d了。

常见的引用类型:slice,pointers,maps,functions和channels,所以如果结构中包含这些将一个对象赋值给另一个对象时候要小心。

这里再多说一句关于golang中 newmake 创建对象的区别:

package main

import (
	"fmt"
)

func main() {
var b = make([]int, 3)
var c = new([]int)
fmt.Printf("%v,%p\n",b,&b)
fmt.Printf("%v,%p\n",c,&c)
}

输入如下:

[0 0 0],0x40a0f0
&[],0x40c138

可以看出, new 返回的是指针并且没有初始值,而 make 则返回的是引用而且有默认值。此外, make 只能创建 slice、map、channelnew 可以用于所有类型的内存分配。

原型模式代码思路如下:

package main

import (
	"encoding/json"
	"fmt"
)

type copy interface {
	copy() Dog
}

type Dog struct {
	name string
	age  int
	food []string
}

func (d Dog) copy() Dog {
	obj := d
	food := new([]string)
	bytes, _ := json.Marshal(d.food)
	_ = json.Unmarshal(bytes, food)
	obj.food = *food
	return obj
}

func main() {
	a := Dog{name: "tom", age: 3, food: []string{"beef","poke"}}
	b := a.copy()
	b.food[0] = "chicken"
	fmt.Printf("a:%v,%p,%p\n",a,&a,&a.food[0])
	fmt.Printf("b:%v,%p,%p\n",b,&b,&b.food[0])
}

这里我使用了 json 包里的序列化函数来模拟deepcopy仅仅为了演示,生产环境下请使用其他方法实现。

应用场景

  1. 当产生对象过程比较复杂,初始化需要许多资源时。
  2. 希望框架原型和产生对象分开时。
  3. 同一个对象可能会供其他调用者同时调用时。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK