原型模式及使用案例
2022-06-02 本文已影响0人
文景大大
一、模式介绍
原型模式是一种创建型的模式,其核心在于快速复制对象。在Java中创建一个对象的方式有如下几种:
- 通过new关键字调用类的构造函数进行创建;
- 通过实现Cloneable接口重写clone方法进行创建;
- 通过反射调用newInstance进行创建;
- 通过反序列化进行对象的创建;
本文的原型模式就是基于第二种方式进行的,该方式是直接基于原始对象在内存中的二进制流直接复制得到拷贝对象,其内容和原始对象完全一致,对比第一种方式,效率更高,无需重复对属性进行赋值操作。原型模式主要适用如下场景:
- 通过构造器创建对象的成本比较大,比如创建过程中时间、CPU、网络资源占用过多;
- 创建一个对象需要繁琐的数据准备或者权限设置等;
- 系统中需要大量使用该对象的副本,且各个调用者需要给它们各自的副本进行属性重新赋值;
原型模式通常实现方式如下:
@Data
public class Person implements Cloneable{
private String name;
private String country;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@Slf4j
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person jack = new Person();
jack.setName("jack");
jack.setCountry("china");
// 原始对象为:Person(name=jack, country=china)
log.info("原始对象为:{}", jack);
Person mason = (Person)jack.clone();
mason.setName("mason");
// 克隆对象为:Person(name=mason, country=china)
log.info("克隆对象为:{}", mason);
// 原始对象为:Person(name=jack, country=china)
log.info("原始对象为:{}", jack);
}
}
这里可以看到,原始对象jack进行clone后,得到的拷贝对象mason是完全复制的其属性的,mason在修改个别属性后,就完成了自己对象的初始化,就可以进行使用了,无论是复制还是修改,效率都比new提升了不少。
需要注意的是,Cloneable接口提供的clone方法,默认实现的是浅拷贝,只有基本数据类型和基本包装类型是完全复制的,对于引用类型则不是完全拷贝的,具体细节可以参考浅拷贝和深拷贝的常见使用方式 - 简书 (jianshu.com)
二、使用案例
2.1 ArrayList
ArrayList中包含了一个clone方法,基本就类似我们上面写的代码:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
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);
}
}
}
根据代码和注释我们也能看到,这是一种浅克隆的原型模式。
2.2 Spring的原型模式
spring默认bean的获取方式是单例的,我们可以通过设置将其改为原型模式,那么每次getBean获取的对象就是完全新的对象。
源码地址尚未找到,后续补充。
三、使用总结
3.1 优点
- 基于内存二进制流复制,性能上高于new一个对象的方式;
- 克隆的对象保存了其属性状态,简化了创建对象的过程。
3.2 缺点
- 需要为每一个类配置clone方法,当类发生变更时,有可能需要修改clone内容,违反了开闭原则;
- 深度克隆需要自己重写clone方法,如果嵌套多层引用对象,实现起来会比较麻烦;