设计模式--原型模式
原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,属于创建型模式。
原型模式的核心在于拷贝原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需在经历时的对象初始化过程(不调用构造函数),性能提升许多。当对象的构建过程比较耗时时,可以利用当前系统中已存在的对象作为原型,对其进行克隆(一般是基于二进制流的复制),躲避初始化过程,使得新对象的创建时间大大减少。
适用场景:
1、类初始化消耗资源较多。
2、new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
3、构造函数比较复杂
4、循环中产生大量对象时。
其中对象的JSON化处理,也是一种原型模式。
@Data
public class ConcreatePrototype implements Cloneable,Serializable {
private int age;
private String name;
private List<String> hobbies;
/*
这个方法是浅拷贝,拷贝数据,不拷贝引用,上面的list集合就没有拷贝,拷贝后的对象的
集合是指向相同的内存空间
*/
@Override
protected ConcreatePrototype clone() {
try {
return (ConcreatePrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
/*
这个方法是深拷贝,拷贝地址,上面的list集合,会拷贝出一个相同内容的数据地址,但对新的内存地址的操作,不会影响到
原来内存的数据
*/
public ConcreatePrototype deepClone() {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
oos.writeObject(this);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream ios = new ObjectInputStream(inputStream);
return (ConcreatePrototype) ios.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "ConcreatePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
克隆破坏单例
如果克隆对象是单例对象。那么,深克隆就会破坏单例,我们可以通过禁止克隆:例如不实现Cloneable接口;要么重写clone()方法,在克隆中返回单例对象。
@Override
protected Object clone() throws CloneNotSupportedException {
return LazyHolder.LAZY;
}
原型模式在源码中的应用
ArrayList类的实现
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
这其实是硬编码的方式,如果对象中申明了很多种集合类型,都要针对每种情况进行单独处理。所以深克隆都采用序列化的方式来操作。
原型模式的优缺点:
优点:
1.性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多。
2、可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到某一历史状态),可辅助实现撤销操作。
缺点:
1、需要为每一个类配置一个克隆方法。
2、克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
3、在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多种嵌套引用时,为了实现深拷贝,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦。
因此,深拷贝、浅拷贝需要根据场景来使用。