原型模式(Prototype Pattern)
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
现实场景:
现在有一份传单,我需要copy100份发送出去。
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 传单类
* @E-Mail : 1029149772@qq.com
* @Date : Created in 10:41 2020-3-16
*/
@Data
@AllArgsConstructor
public class Leaflet {
private String name;
private String content;
private Date createTime;
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : 1029149772@qq.com
* @Date : Created in 10:43 2020-3-16
*/
public class Client {
public static void main(String[] args) {
//第一种方法,我要3张一样的传单
Date date = new Date();
Leaflet leaflet1 = new Leaflet("健身房传单", "游泳健身了解一下", date);
Leaflet leaflet2 = new Leaflet("健身房传单", "游泳健身了解一下", date);
Leaflet leaflet3 = new Leaflet("健身房传单", "游泳健身了解一下", date);
System.out.println(leaflet1+"\n"+leaflet2+"\n"+leaflet3);
}
}
好的,现在我们实现了这个需求,无非就是new100个传单对象罢了,小事,但是现在问题来了,如果我们传单的内容很多呢,比如里面还有好多属性,然后还有list之类的,那么每次new是不是都要写一大串的代码呢,copy出来手也类啊,而且代码多难看不是。
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 传单类
* @E-Mail : 1029149772@qq.com
* @Date : Created in 10:41 2020-3-16
*/
@Data
@AllArgsConstructor
public class Leaflet implements Cloneable{
private String name;
private String content;
private Date createTime;
//只需要实现Cloneable接口,重写Object的clone方法就可以克隆了
//浅克隆
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : 1029149772@qq.com
* @Date : Created in 10:43 2020-3-16
*/
public class Client {
public static void main(String[] args) {
Date date = new Date();
Leaflet leaflet1 = new Leaflet("健身房传单", "游泳健身了解一下", date);
try {
Leaflet leaflet2 = (Leaflet) leaflet1.clone();
Leaflet leaflet3 = (Leaflet) leaflet1.clone();
System.out.println(leaflet1+"\n"+leaflet2+"\n"+leaflet3);
System.out.println("----------------------------------------------");
//此时我只改变传单1的内容
date.setTime(251651561);
System.out.println(leaflet1+"\n"+leaflet2+"\n"+leaflet3);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
可以看到,我们用clone确实实现了不需要new就能添加相同传单的目的,但是问题又来了,我们只是把Date改变了一下,结果不止是传单一的时间改变了,传单二和三的时间也改变了,此时又来了新的需求,要把每一批传单的时间分开来,怎么实现呢。
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 传单类
* @E-Mail : 1029149772@qq.com
* @Date : Created in 10:41 2020-3-16
*/
@Data
@AllArgsConstructor
public class Leaflet implements Cloneable{
private String name;
private String content;
private Date createTime;
//只需要实现Cloneable接口,重写Object的clone方法就可以克隆了
//浅克隆
/*@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}*/
//深克隆,当然有其它的实现方法比如序列化和反序列化,但我们这里先用最简单的一种来演示
@Override
protected Object clone() throws CloneNotSupportedException {
Leaflet leaflet =(Leaflet) super.clone();
//实现深克隆,把引用对象,数组,容器等都克隆一遍
leaflet.createTime = (Date)this.createTime.clone();
return leaflet;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : 1029149772@qq.com
* @Date : Created in 10:43 2020-3-16
*/
public class Client {
public static void main(String[] args) {
Date date = new Date();
Leaflet leaflet1 = new Leaflet("健身房传单", "游泳健身了解一下", date);
try {
Leaflet leaflet2 = (Leaflet) leaflet1.clone();
Leaflet leaflet3 = (Leaflet) leaflet1.clone();
System.out.println(leaflet1+"\n"+leaflet2+"\n"+leaflet3);
System.out.println("----------------------------------------------");
//此时我只改变传单1的内容
date.setTime(251651561);
System.out.println(leaflet1+"\n"+leaflet2+"\n"+leaflet3);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
这样,我们就发现了,现在克隆出来的每一个对象都是一个完全的独立个体了,我改第一个订单里面的date对象,下两个的传单中的date对象却没有改变,这是为什么呢?
这里我们就要引入两个概念了:
浅克隆:
浅克隆就是把原对象v1的内存完全copy一份,在堆中开辟一个新的空间放置,里面的引用对象也只是复制了它的classpath,并不会对v1中的所有引用对象再重新开辟一个空间,这样就导致了这两个对象除了基本类型以外,其余类型的成员变量还是指向了同一块内存地址,那么当这个地址被改变的时候,v1,v2中的对应成员变量也会跟着改变(引用关系如下图)。
深克隆:
而深克隆刚好就是为了解决这个问题,它会把v1中的引用类型等的成员变量也copy一份出来,放到一个新的地址中,然后v2这些成员变量的classpath指向这个新copy出来的地址,这样被copy出来的对象也就完全独立开来了(引用关系如下图)。
总结:
优点:
1.通过复制现有的实例来创建相关的实例 ,无需知道相应类的信息(隐藏了制造新实例的复杂性,比如一个类初始化需要进行很多步骤,有了他完全不用关心)。
2.重复创建相似的对象的时候可以使用,高效很多,省代码。
缺点:
1.每一个类必须配备一个克隆方法
2.深层复制比较复杂(深克隆)
注意事项:
1.使用原型模式复制对象不会调用类的构造方法,它是直接取到引用的那一块内存原样拷贝到一个新开辟的内存空间中,所以,单例模式和原型模式是冲突的,使用时要特别注意。
2.Object类的clone方法只会克隆对象中的基本数据类型,对于数组,容器对象,引用对象等都不会克隆,这就是浅克隆,如果要实现深克隆,必须把这些另行克隆。
附上彩蛋:
设计模式总结:
spring中编程思想总结:
springaop必须知道的几个概念: