介绍
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。我们在程序设计的时候通常会创建很多对象,并且随着不断地开发,会发现在使用过程很多对象是相似甚至是相同的,如果没有使用原型模式,那么我们需要进行下面类似的操作(翻出以前的代码),大量的set方法,这代码看起来十分的“工整”,“干净”,但是全部是无脑式的体力劳动,没有半点技术含量,这种方式不仅浪费了大量的时间去写,其效率还不高,因为需要进行对象的初始化,还要一个一个的赋值,原型模式就是为了来解决这种无脑式的操作。
想必克隆这个词我们大家都很熟悉,1996年在英国爱丁堡市罗斯林研究所克隆出了世界上第一个克隆生物-克隆羊多利,我们的原型模式也是运用了克隆这个思想,下面我们都将其称为拷贝,原型模式就是通过拷贝原型对象,然后再创建出一个同拷贝对象一样的对象(但是它们并不是同一个对象,只是类型相同罢了),只不过他不是通过对象的构造函数进行赋值而来的,而是基于内存的二进制流进行复制,所以效率比new一个对象进行复制要高效。
Java为我们内置了原型接口(Cloneable),如果要进行对象的拷贝,必须实现Cloneable接口,它是一个空接口,里面没有任何的方法,它只是一个规约而已,我们实现Cloneable接口只需要在类里面重写Object的clone()方法,这样我们就可以进行对象的拷贝,注意:如果没有实现Cloneable接口,直接使用Object的clone()方法,那么将会抛出异常(CloneNotSupportedException),因为JVM里面识别该类没有实现Cloneable接口,不能安全的进行拷贝,我们看一下它们的实现关系
下面我们进行实际的操作:
假如我有一个女朋友,她的身高170cm,体重55kg,胸围90,人长得漂亮,就是不太温柔,因为和她在一起的时间的长了,所以有些腻了,不过我还是喜欢她的身高和美貌,我想复制出一个身高,体重,胸围和她一样的女孩子,只不过我希望复制出的女孩子非常温柔,善解人意,因为我手里有原型工具,所以我直接开始了,我按照自己的要求输入,然后等待复制,不一会儿,我想要的女孩子出来了,它们一模一样,但是复制出来的女孩子非常温柔。我们具体代码实现一下。
一.首先定义一个原型类;让其实现Cloneable接口,兵重写Object clone()方法
我们充clone()方法中看出它使用了super.clone()进行拷贝,super.clone()是直接使用内存的二进制流进行复制,在复制的时候会为目标对象分配一块内存,所以它们的地址是不相同的,只是类型是相同的。
由上看出clone()方法由native修饰,可知它调用的不是java层面的方法,而是JVM里面的方法(JVM部分是使用C++进行编写),自然不是基于java层面的构造函数进行复制。
二.客户端调用
由图可知,我已经复制出一个我喜欢的类型的新女朋友了,她和我原女友在生理上有相同的基因,只不过她很温柔体贴,是我喜爱的类型,到了某一天,我根据她们两个的性格给她们两个安排不同的任务,首先,她们两个都要会做饭,还要会洗衣服,由于原女友脾气火爆,我不让她给我按摩洗脚,我让温柔的新女友给我按摩洗脚,我又一次对她们两个进行改造,我让她们两个开始做任务了。
但是这下出意外了,大事不好了,我只是让新女友给我按摩洗脚,现在原女友也给我按摩洗脚,他脾气那么火爆,怕是要把我搞死,我仔细想了一下问题出在哪里,苦思冥想许久,原来她们的身高,体重,胸围这些是基本的要求(基本类型),但是她们的任务就不是基本类型了(引用类型),所以在改造过程中出现了问题,导致不该分配的任务分配给了原女友,于是查了很多资料后原来我是对她们进行了浅拷贝,所以才会这样,为了我的生命安危,我需要再次进行改造。
对她们进行深拷贝
一,对原型类进行一个改造,加上了序列化接口Serializable,它也是一个空接口,我们写了deepClone()方法,使用流和序列化的方式进行复制。
二,客户端调用。
发现,原女友没有按摩洗脚的任务了,小命终于保住了,使用了深克隆后,我们发现她们两个的任务不再一样了,为什么之前使用浅克隆是一样的呢,因为她们的任务是一个引用类型,使用浅克隆时复制的其实不是它们的值,而是它们的地址,使用了深克隆后,它们是以流的形式进行复制。
以上是我通过复制女朋友的例子来理解原型模式,只不过现实中我依然还是个单身狗,其实从上面的例子中我们发现只有身高,体重,胸围这三个属性的值没有发生变化,其他的属性我们都重新进行了赋值,那么你可能会说,这没必要使用原型模式吧,是的,少得话可能体现不出来,但是如果这个类的属性特别多,又特别复杂的时候,你还会选择一个一个的取set吗?