原型模式的定义与特点
原型(Prototype
)模式的定义:
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
原型模式的结构
原型模式包含以下主要角色:
抽象原型类
:规定了具体原型对象必须实现的接口。
具体原型类
:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类
:使用具体原型类中的 克隆方法来复制新的对象。
原型模式的实现
原型模式的克隆分为浅克隆
和深克隆
原型模式通常适用于以下场景:
1.对象之间相同或相似,即只是个别的几个属性不同的时候。
2.对象的创建过程比较麻烦,但复制比较简单的时候。
浅克隆
Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable
接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。其代码如下:
具体原型类:
/**
* 学生类
*/
@Data
public class Student implements Cloneable {
private String name;//姓名
private int age;//年龄
private ArrayList<String> hobby;//爱好
/**
* 浅克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
}
浅克隆测试类:
public class StudentTest {
@Test
public void test() throws Exception {
Student student = new Student();
student.setName("张三");
student.setAge(18);
Student student2 = student.clone();
System.out.println("原型对象:" + student);
System.out.println("克隆对象:" + student2);
System.out.println(student == student2);
}
}
运行结果:
看起来没毛病啊,和我们预期一样,克隆出一模一样内容的对象,而且地址不相同。
其实是有个问题,暂时还没暴露,下边说说浅克隆带来的问题。
浅克隆带来的问题
注意看,上边的测试代码其实是没有给爱好赋值的。我们来给他加点爱好看看就知道问题在哪了。
测试代码:
public class StudentTest {
@Test
public void test() throws Exception {
Student student = new Student();
ArrayList<String> hobby = new ArrayList<String>();
hobby.add("书法");
hobby.add("画画");
student.setName("张三");
student.setAge(18);
student.setHobby(hobby);//给原型对象添加爱好
Student student2 = student.clone();
student2.getHobby().add("看书");//给克隆的对象增加一个爱好
System.out.println("原型对象:" + student);
System.out.println("克隆对象:" + student2);
System.out.println(student == student2);
}
}
运行结果:
发现问题没?我明明是给克隆出来的对象增加了一个“看书”的爱好,怎么原型对象的爱好也跟着变了呢?这就是浅克隆带来的问题。
下边就轮到深克隆出场来解决这个问题了。
深克隆
深克隆常见的实现方式有两种:
序列化实现深克隆
具体原型类:
和之前不变,只需实现Serializable
接口新增深克隆方法就行。
@Data
public class Student implements Cloneable , Serializable {
private String name;//姓名
private int age;//年龄
private ArrayList<String> hobby;//爱好
/**
* 浅克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
/**
* 序列化深克隆方法
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Student deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream ous = new ObjectOutputStream(bos);
ous.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student)ois.readObject();
}
}
测试代码:
public class StudentTest {
@Test
public void test() throws Exception {
Student student = new Student();
ArrayList<String> hobby = new ArrayList<String>();
hobby.add("书法");
hobby.add("画画");
student.setName("张三");
student.setAge(18);
student.setHobby(hobby);//给原型对象添加爱好
Student student2 = student.deepClone();//调用深克隆的方法
student2.getHobby().add("看书");//给克隆的对象增加一个爱好
System.out.println("原型对象:" + student);
System.out.println("克隆对象:" + student2);
System.out.println(student == student2);
}
}
测试结果:
json实现深克隆
具体原型类:
@Data
public class Student implements Cloneable , Serializable {
private String name;//姓名
private int age;//年龄
private ArrayList<String> hobby;//爱好
/**
* 浅克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
/**
* json深克隆方法
* @return
*/
public Student jsonDeepClone(){
String json = JSON.toJSONString(this);
Student student = JSON.parseObject(json, Student.class);
return student;
}
}
测试结果:也是没毛病的
原型模式总结
优点:
1.性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象的性能上提升了很多。
2.可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,将其保存起来,简化了创建过程。
缺点:
1.必须要有克隆(或者拷贝)的方法
2.当对已有类进行改造时,需要修改代码,违法开闭原则
注意:
深拷贝和浅拷贝需要运用得当