设计模式 05 原型模式
原型模式(Prototype Pattern)属于创建型模式
概述
原型模式实际上就是对象的拷贝。
原型模式使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。也就是说,原型对象作为模板,通过克隆操作,来产生更多的对象,就像细胞的复制一样。
原型模式的拷贝分为浅拷贝和深拷贝。
代码实现
浅拷贝
对于类中基本数据类型,会直接复制值给拷贝对象。
对于类中引用类型,只会复制对象的地址,而实际上指向的还是原来的那个对象。
// 基本类型浅拷贝
int a = 10;
int b = a;
// 输出:true
System.out.println(a == b);
// 引用类型浅拷贝,拷贝的仅仅是对上面对象的引用
Object o = new Object();
Object k = o;
// 输出:true
System.out.println(o == k);
在 Java 中,就可以实现 Cloneable 接口提供的拷贝机制,来实现原型模式:
1、定义实体类实现 Cloneable 接口
/**
* 用户
* <p>注意需要实现 Cloneable 接口
*/
public class Student implements Cloneable {
String name;
public Student(String name){
this.name = name;
}
public String getName() {
return name;
}
/**
* 提升clone方法的访问权限
* @return 对象
* @throws CloneNotSupportedException 不支持克隆异常
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2、来看看克隆的对象是不是原来的对象
Student student0 = new Student();
Student student1 = (Student) student0.clone();
System.out.println(student0);
System.out.println(student1);
可以看到,通过 clone()
方法克隆的对象并不是原来的对象,我们来看看如果对象内部有属性会不会一起进行克隆:
public class Student implements Cloneable{
String name;
public Student(String name){
this.name = name;
}
public String getName() {
return name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Student student1 = new Student("程序航");
Student student2 = (Student) student1.clone();
// 输出:true
System.out.println(student1.getName() == student2.getName());
// 输出:false
System.out.println(student1 == student2);
可以看到,虽然 Student 对象成功拷贝,但是其内层对象并没有进行拷贝,依然只是对象引用的复制。所以 Java 为我们提供的 clone
方法只会进行浅拷贝。那么如何才能实现深拷贝呢?
深拷贝
无论是基本类型还是引用类型,深拷贝会将引用类型的所有内容,全部拷贝为一个新的对象,包括对象内部的所有成员变量,也会进行拷贝。
1、对成员变量也进行拷贝
@Override
public Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
// 这里我们改进一下,针对成员变量也进行拷贝
student.name = new String(name);
// 成员拷贝完成后,再返回
return student;
}
2、再执行上述的代码
Student student1 = new Student("程序航");
Student student2 = (Student) student1.clone();
// 输出:false
System.out.println(student1.getName() == student2.getName());
// 输出:false
System.out.println(student1 == student2);
可以看到, Student 对象和其中的属性 name 都进行了拷贝,是完全不一样的对象了。这样就是拷贝生成了一个全新的对象,也就是深拷贝了。
优缺点
优点
1、性能提高。
2、逃避构造函数的约束。
缺点
1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
2、必须实现 Cloneable
接口。
使用场景
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new
产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone
的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,可以随手拿来使用。
注意事项
与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable
,重写 clone
方法;深拷贝是通过实现 Serializable
读取二进制流。
参考
https://www.bilibili.com/video/BV1mc411h719?p=6&vd_source=299f4bc123b19e7d6f66fefd8f124a03