上篇:
下篇:
写代码大家最常用的两个快捷键是什么?当然是复制粘贴!
因为又简单又爽。
当然这样的后果就是造成工程代码中大量的代码重复,维护极其困难,一点一点的,屎山代码就出现了。
那么有没有一种方法可以名正言顺的复制粘贴呢?
当然有了,那就是原型模式。原型模式也叫克隆模式。
原型模式的定义
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式是一种非常非常常用的模式,在实际工作场景中,会经常遇到结构相似的数据结构,我们需要对这些数据进行不同的操作,因此需要拷贝数据。保证数据的值相同但是内存地址不同,每次拷贝其实就是一次原型模式的实现。
原型模式的目的就是快速创建一个已有对象的克隆,如果在直接创建数据需要的开销比较大时就会选择原型模式。
原型模式其实可以看做是一种工厂模式的实现,工厂就是数据原型,产品就是新拷贝的数据。
原型模式的使用流程
简单的原型模式流程
- 定义一个抽象接口,一般叫做Clone(),作用是创建已有对象的克隆。该接口被称为抽象原型类(Prototype),可以类比为工厂模式中的抽象工厂。
- 确定原型对象,原型对象称为具体原型类(ConcretePrototype)。可以类比为工厂模式中的具体工厂。
- 为每个原型对象实现抽象接口方法Clone()。这样每个原型就拥有了拷贝的方法。可以进行复制了。
- 客户端需要获取一份新拷贝时,直接调用需要的原型对象的Clone()方法。
这时候使用已经没有太大问题了。
但是随着原型对象数量越来越多,人们开始发现另一个问题。
我客户端就想要一个拷贝值,难道我还得知道有啥原型对象才行?
所以人们为了更好的职责分离,提出了原型管理器。
加了原型管理器的原型模式流程
原型管理器(Prototype Manager):将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得。在原型管理器中针对抽象原型类进行编程,以便扩展。
加了原型管理器的原型模式流程如下:
- 定义一个抽象接口,一般叫做Clone(),作用是创建已有对象的克隆。该接口被称为抽象原型类(Prototype),可以类比为工厂模式中的抽象工厂。
- 确定原型对象,原型对象称为具体原型类(ConcretePrototype)。可以类比为工厂模式中的具体工厂。
- 为每个原型对象实现抽象接口方法Clone()。这样每个原型就拥有了拷贝的方法。可以进行复制了。
- 创建一个原型管理器,将所有的原型对象加到里面,可以直接通过索引获取到对应的原型对象。索引可以是整数,字符串,地址等等。
- 客户端需要获取一份新拷贝时,直接使用原型管理器获取。
原型模式的简单实现代码参考
package prototype
// Cloneable接口,动物必须实现这个接口
type Cloneable interface {
Clone() Cloneable
}
// 羊
type Sheep struct {
name string
weight int
}
func (s *Sheep) Clone() Cloneable {
tc := *s
return &tc
}
// 牛
type Cow struct {
name string
gender bool
}
func (c *Cow) Clone() Cloneable {
newCow := &Cow{
name: c.name,
gender: c.gender,
}
return newCow
}
// 原型管理器
type CloneLab struct {
animals map[string]Cloneable
}
func NewPrototypeManager(name []string, protos []Cloneable) *CloneLab {
cloneLab:=&CloneLab{animals:make(map[string]Cloneable)}
for i:=range name{
cloneLab.animals[name[i]]=protos[i]
}
return cloneLab
}
// 客户端
func main() {
lab := NewCloneLab()
lab.Set("cow", &Cow{name: "i am cow", gender: true})
c := lab.Get("cow").Clone()
}
原型模式的适用场景
在以下情况下可以考虑使用原型模式:
- 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
- 系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
- 复制原型对象得到新实例比使用构造函数创建一个新实例更加方便时。
原型模式的优缺点
主要优点
原型模式的主要优点如下:
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
- 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
- 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
- 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
主要缺点
原型模式的主要缺点如下:
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。
参考文献
对象的克隆——原型模式(四)_LoveLion的博客-CSDN博客