原型模式(Prototype)
在有些系统中,存在大量相同或相似对象的创建问题,使用构造函数来创建对象复杂且耗时耗资源,这时候就可以复制一个原型来生成新对象,这就是所谓的原型模式。
定义与特点
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
优缺点
优点:
- java的clone方法基于内存二进制流的复制,性能上比new一个对象要要高。
- 可以使用深克隆保存一个对象的状态,以便于需要的时候再使用,可以辅助实现撤销操作。
缺点:
- 每个类都要重写clone方法
- clone方法必须要在类的内部,对已有类改造的时候需要修改代码,违背了开闭原则
- 需要实现深克隆时需要编写复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现复杂(尤其是将现有项目改造为克隆模式时)。因此深克隆和浅克隆之间要做好取舍。
应用场景
- 类需要高频的创建或类初始化要消耗大量敏感资源(或者只是简单的资源优化),原型模式实例化效率高于new关键字实例化。(敏感资源指在意的资源)
- 类的属性过多但属性组合及其有限时(或者其中某些属性的组合有限甚至只有一种)可以创建对应组合数目的原型并克隆免于繁琐的属性装配
- 需要高频的创建相似对象,使用原型模式不但可以简化创建过程,而且可以提高系统性能。
- 一个对象需要提供给多个对象调用且多个对象均有修改需求
原型模式较少单独使用,常需要配合其他设计模式
(可以根据需求选择重写equals方法使得克隆前后对象equals对方返回值均为true)
结构与实现
结构
角色:
- 抽象原型类(Prototype):规定了具体原型类需要实现的方法(可以是接口,抽象类或者具体类)
- 具体原型类(RealPrototype):实现clone方法的具体被复制对象
- 访问类(Client):调用具体原型类的clone方法来复制新的对象
实现
java的Object类就已经提供了clone方法,具体原型类只需要实现Cloneable接口并重写clone方法就可以实现对象的克隆。这里的Cloneable接口仅仅起一个标记的作用,表示这个对象可以克隆,其内部并没有进行任何操作。
public class RealPrototype implements Cloneable{
public RealPrototype clone() throws CloneNotSupportedException {
return (RealPrototype) super.clone();
}
}
//这里的抽象原型类是Object,因为Cloneable没有clone方法
问题
深克隆与浅克隆
Object类的clone方法是浅拷贝,只会克隆对象中的基本数据类型,数组和引用对象只复制了引用地址。
要实现深拷贝,需要再次克隆原型模式的数组、引用对象。
java中的八种基本类型会深拷贝。特殊的,八种基本类型的包装类和String为不可变类,可以视为深拷贝。
调用引用类型属性的clone方法
List<String> realPrototypeList;
@Override
public RealPrototype clone() throws CloneNotSupportedException {
RealPrototype realPrototype = (RealPrototype) super.clone();
realPrototype.realPrototypeList = (List<String>) ((ArrayList)realPrototype.realPrototypeList).clone();
return realPrototype;
}
这样可以实现深克隆,但是对于有多个引用类型属性尤其是属性为容器类且容器类内部元素也是引用类型的类处理起来就相当麻烦,需要递归调用clone方法,对于这样的类可以采用反序列化来实现深克隆。
反序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (RealPrototype)ois.readObject();
构造方法
因为对象的实例化是通过clone方法实现的,因此类的构造方法不会被调用,所以类的构造方法权限也被直接无视。