介绍
原型模式是一种创建型设计模式,其主要目的是通过复制(克隆)现有对象来创建新对象,而无需使用显式的构造函数,即无需关心具体对象的构造过程,从而避免了创建对象时的耗时操作,提高了性能。
在原型模式中,原型对象是可被复制的对象,通常实现了一个Clone方法,用于复制自身并创建一个新的对象。新对象可以是完全独立的,也可以与原型对象共享部分属性。
- 原型接口(Prototype):定义了克隆方法 Clone(),所有实现该接口的具体原型类都要实现该方法。
- 具体原型类(Concrete Prototype):实现了原型接口,并提供了克隆方法的具体实现。
优点
- 通过clone来创建对象,提高了对象的创建效率。
- 克隆原型对象时只需要进行必要的属性赋值,而不需要再执行复杂的初始化过程。
- 通过克隆原型对象,可以动态地生成新的对象,而无需事先确定具体的对象类。
缺点
- 需要为每个具体原型类实现克隆方法,这增加了代码量和维护成本。
- 如果原型对象与其他对象存在关联关系,那么克隆对象也会受到关联对象的影响。
使用场景
原型模式用于创建重复的对象。当一个类在创建时开销比较大时(比如大量数据准备,数据库连接),我们可以缓存该对象,当下一次调用时,返回该对象的克隆。
深拷贝和浅拷贝
Clone可实现深拷贝或者浅拷贝。浅拷贝是指被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。深拷贝把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
浅拷贝代码示例
package main
import "fmt"
// 定义原型接口
type ShapePrototype interface {
Clone() ShapePrototype
GetType() string
}
// 定义具体原型类 Rectangle
type Rectangle struct {
Type string
}
func (r *Rectangle) Clone() ShapePrototype {
return &Rectangle{
Type: r.Type,
}
}
func (r *Rectangle) GetType() string {
return r.Type
}
// 定义具体原型类 Circle
type Circle struct {
Type string
}
func (c *Circle) Clone() ShapePrototype {
return &Circle{
Type: c.Type,
}
}
func (c *Circle) GetType() string {
return c.Type
}
func main() {
// 创建矩形原型对象
rectanglePrototype := &Rectangle{
Type: "Rectangle",
}
fmt.Println("Rectangle Type:", rectanglePrototype.GetType())
// 克隆矩形原型对象来创建新对象
rectangle := rectanglePrototype.Clone().(*Rectangle)
fmt.Println("Rectangle Type:", rectangle.GetType())
// 创建圆形原型对象
circlePrototype := &Circle{
Type: "Circle",
}
fmt.Println("Circle Type:", circlePrototype.GetType())
// 克隆圆形原型对象来创建新对象
circle := circlePrototype.Clone().(*Circle)
fmt.Println("Circle Type:", circle.GetType())
}
在上述代码中,通过调用Clone()
方法,可以很方便的创建新对象。Clone()
方法返回了一个新的Rectangle
对象,并将原对象的Type
属性赋值给新对象的Type
属性。这是一个浅拷贝,因为没有递归地复制其他对象。
深拷贝代码示例
如果要实现深拷贝,需要在原型对象和克隆对象之间复制引用类型的属性,并创建独立的副本。
下面是一个深拷贝的示例:
// 具体原型类 Rectangle
package main
import (
"fmt"
)
// 原型接口
type ShapePrototypes interface {
Clone() ShapePrototypes
}
// 具体原型类 - 矩形
type RectAngle struct {
Type string
Data []int // 引用类型属性
}
func (r *RectAngle) Clone() ShapePrototypes {
// 创建新对象
clone := &RectAngle{
Type: r.Type,
}
// 复制引用类型属性
clone.Data = make([]int, len(r.Data))
copy(clone.Data, r.Data)
return clone
}
func main() {
// 创建原型对象
rectanglePrototype := &RectAngle{
Type: "Rectangle",
Data: []int{1, 2, 3},
}
// 克隆原型对象来创建新对象
rectangle := rectanglePrototype.Clone().(*RectAngle)
// 修改克隆对象的属性
rectangle.Data[0] = 100
// 打印原型对象和克隆对象的属性
fmt.Println(rectanglePrototype.Data) // [1 2 3]
fmt.Println(rectangle.Data) // [100 2 3]
fmt.Println(rectanglePrototype.Data) // [1 2 3]
}
可以看到,克隆对象的Data属性修改后,并不影响到原型对象的Data属性,因为这是独立的副本。这证明了深拷贝的实现。