源码:https://github.com/2020GetGoodOffer/springStudy
原型模式的应用场景
来看一段代码:
public void setParam(ExamPaperVo vo){
ExamPaper examPaper=new ExamPaper();
//试卷id
examPaper.setId(vo.getId());
//试卷剩余时间
examPaper.setLeaveTime(vo.getLeaveTime());
//试卷姓名
examPaper.setName(vo.getName());
.....
//省略数十行
}
该代码非常公正,命名也很规范,注释也很全,但是这样的代码属于纯体力劳动,原型模式就能替我们解决这样的问题。
原型模式是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
原型模式主要适用于以下场景:
(1)类初始化消耗资源较多
(2)使用new生成一个对象需要非常繁琐的过程(数据准备、访问权限等)
(3)构造方法比较复杂
(4)在循环体中产生大量对象
在Spring中,原型模式应用的非常广泛。
例如scope=“prototype”,经常使用的JSON.parseObject()也是一种原型模式。
浅克隆
一个标准的原型模式代码应该是这样设计的,先创建原型Prototype接口:
public interface Prototype {
Prototype clone();
}
创建具体需要克隆的类ConcretePrototypeA:
public class ConcretePrototypeA implements Prototype {
private int age;
private String name;
private List<String> hobbies;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
@Override
public Prototype clone() {
ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
concretePrototypeA.setAge(this.age);
concretePrototypeA.setName(this.name);
concretePrototypeA.setHobbies(this.hobbies);
return concretePrototypeA;
}
}
创建Client类:
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype=prototype;
}
public Prototype startClone(Prototype prototype){
return prototype.clone();
}
}
测试代码:
public static void main(String[] args) {
//创建需要克隆的对象
ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
//填充属性
concretePrototypeA.setAge(18);
concretePrototypeA.setName("prototype");
concretePrototypeA.setHobbies(new ArrayList<>());
System.out.println(concretePrototypeA);
//克隆
Client client=new Client(concretePrototypeA);
ConcretePrototypeA concretePrototypeA1= (ConcretePrototypeA) client.startClone();
System.out.println(concretePrototypeA1);
System.out.println("克隆对象中引用类型地址:"+concretePrototypeA1.getHobbies().hashCode());
System.out.println("原对象中引用类型地址:"+concretePrototypeA.getHobbies().hashCode());
}
运行结果:
可以看出引用地址是相同的,意味着复制的不是值,而是引用的地址。这样的话如果修改任意一个对象的属性值,另一个对象的hobbies值都会改变。
这就是浅克隆,浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之所有引用对象仍然指向原来的对象。
深克隆
我们换一个场景,以孙悟空为例:
原型猴子类:
public class Monkey {
public int height;
public int weight;
public Date birthday;
}
创建引用对象金箍棒类:
public class Bang implements Serializable {
public float h=100;
public float d=10;
public void big(){
this.h*=2;
this.d*=2;
}
public void small(){
this.h/=2;
this.d/=2;
}
}
创建具体的孙悟空类:
public class SunWuKong extends Monkey implements Cloneable, Serializable {
public Bang bang;
public SunWuKong(){
this.birthday=new Date();
this.bang=new Bang();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
public Object deepClone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
SunWuKong copy= (SunWuKong) ois.readObject();
copy.birthday=new Date();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
public SunWuKong shallowClone(SunWuKong target){
SunWuKong sunWuKong = new SunWuKong();
sunWuKong.height=target.height;
sunWuKong.weight=target.weight;
sunWuKong.birthday=new Date();
sunWuKong.bang=target.bang;
return sunWuKong;
}
}
测试代码:
public class Test {
public static void main(String[] args) {
SunWuKong sunWuKong = new SunWuKong();
try{
SunWuKong clone= (SunWuKong) sunWuKong.clone();
System.out.println("深克隆:"+(sunWuKong.bang==clone.bang));
}catch (Exception e){
e.printStackTrace();
}
SunWuKong sunWuKong1 = new SunWuKong();
SunWuKong sunWuKong2 = sunWuKong1.shallowClone(sunWuKong1);
System.out.println("浅克隆:"+(sunWuKong1.bang==sunWuKong2.bang));
}
}
运行结果:
克隆破坏单例模式
如果我们克隆的对象是单例对象,那么意味着深克隆会破坏单例模式。
实际上防止克隆破坏单例模式的思路很简单,禁止深克隆即可。
要么我们的单例类不实现Cloneable接口,要么我们重写clone方法,在clone方法中返回单例对象即可,具体代码如下:
@Override
protected Object clone() throws CloneNotSupportedException{
return INSTANCE;
}
clone方法的源码
常用的ArrayList实现了Cloneable接口