1、什么是原型模式
在讲解什么是原型模式之前,先看看我们项目中是否会经常使用get和set进行赋值场景,比如:接收参数paramVo对象,然后将参数赋值给另外一个对象
public void saveParam(ParamVo vo){
//为了证明ParamVo意义,验证确认密码
if(!vo.getConfirmPassword().equals(vo.getPassword())){
System.out.println("密码和确认密码不一致");
return;
}
Param param = new Param();
//设置登录名
param.setName(vo.getName());
//设置密码
param.setPassword(vo.getPassword());
//更多赋值项
...
//TODO 进行Param 保存
}
上述代码相信是很多项目中经常看到的,一旦赋值参数很多,是否觉得这类操作很繁琐,如果使用原型是原型模式就可以快速解决,即原型模式是指用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
原型模式通常适用于以下场景:
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
- 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
原型模式的克隆分为浅克隆和深克隆。
2、浅克隆
浅克隆是在指创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
下面我们来设计一个浅克隆模型,Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类
public class ConcretePrototype implements Cloneable {
//基本类型属性
private int age;
//非基本类型属性
private List hobbys;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List getHobbys() {
return hobbys;
}
public void setHobbys(List hobbys) {
this.hobbys = hobbys;
}
ConcretePrototype(){
System.out.println("具体原型创建成功!");
}
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (ConcretePrototype) super.clone();
}
测试代码如下:
public class ShallowCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
ConcretePrototype obj1 = new ConcretePrototype();
obj1.setAge(18);
obj1.setHobbys(Arrays.asList("打球","洗脚"));
ConcretePrototype obj2 = (ConcretePrototype) obj1.clone();
System.out.println("克隆对象obj1中引用类型地址值:" + obj1.getHobbys());
System.out.println("克隆对象obj2中引用类型地址值:" + obj2.getHobbys());
System.out.println("对象地址比较:" + (obj1.getHobbys() == obj2.getHobbys()));
}
}
3、深克隆
深克隆是指创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。和浅克隆不一样的是不再指向相同内存地址而是使用序列化与反序列化(不同内存地址)
我们用案例说说明一下:新建两个类,课程类和视频类,每个课程都有相应的视频
视频类:
public class Video implements Serializable {
private String videoName;
}
课程类:
public class Course implements Cloneable, Serializable {
public String courseName;
public Video video;
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public Video getVideo() {
return video;
}
public void setVideo(Video video) {
this.video = video;
}
@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);
Course copy = (Course)ois.readObject();
return copy;
} catch (Exception e){
e.printStackTrace();
return null;
}
}
public Course shallowClone(Course target) {
Course course = new Course();
course.video = target.video;
course.courseName = target.courseName;
return course;
}
}
测试代码:
public class DeepCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Course course = new Course();
course.setCourseName("JAVA");
course.setVideo(new Video());
try{
Course clone = (Course)course.clone();
System.out.println("深克隆内存地址是否相同:"+(course.video == clone.video));
} catch (Exception e){
e.printStackTrace();
}
Course q = new Course();
Course n = q.shallowClone(q);
System.out.println("浅克隆内存地址是否相同:"+(q.video == n.video));
}
}
观察深克隆和浅克隆的内存地址:
4、结语
通过上述的讲解以及代码演示,我们可以总结出原型模式的优缺点:
优点:
- Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
至此本文介绍了原型模式的浅克隆和深克隆,并以实际的现实案例作为代码演示,通过本章节希望对大家对原型模式有一个更清晰的理解;