1. 原型模式概述
1.1 原型概念
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
1.2 原型优缺点
1.原型模式的优点:
Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
2.原型模式的缺点:
需要为每一个类都配置一个 clone 方法 clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
1.3 使用场景
定义已经说得很清楚,便于创建相同对象或者相似对象。
1.4 深浅克隆区别
原型模式的克隆分为浅克隆和深克隆。
1.浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
2.深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
2. 原型模式实现
/**
* @author zrj
* @date 2020/12/9
* @since V1.0
**/
@Getter
@Setter
@ToString
public class Employee implements Cloneable {
private String name;
private int age;
/**
* 地址对象
* 浅复制:Address未实现CloneAble接口,没有重写clone方法
*/
private Address address;
/**
* 工作经历
* 深复制:WorkStation实现CloneAble接口,重写clone方法
*/
private WorkStation workStation;
public Employee(String name, int age, Address address, WorkStation workStation) {
this.name = name;
this.age = age;
this.address = address;
this.workStation = workStation;
}
public void setAddressInfo(String code, String name) {
address.setAddressCode( code );
address.setAddressName( name );
}
public void setWorkStation(String code, String name) {
workStation.setWorkCode( code );
workStation.setWorkStation( name );
}
public void showAddress() {
System.out.println( "------------------------------" );
System.out.println( "姓名:" + name + ",年龄:" + age + ",地址:" + address.getAddressName() + ",工作:" + workStation.getWorkStation() );
}
@Override
public Employee clone() throws CloneNotSupportedException {
return (Employee) super.clone();
}
public Employee deepClone() throws CloneNotSupportedException {
Employee deepClone = (Employee) super.clone();
deepClone.setWorkStation( workStation.clone() );
return deepClone;
}
}
/**
* @author zrj
* @date 2020/12/9
* @since V1.0
**/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String addressCode;
private String addressName;
}
/**
* @author zrj
* @date 2020/12/9
* @since V1.0
**/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class WorkStation implements Cloneable{
private String workCode;
private String workStation;
@Override
public WorkStation clone() throws CloneNotSupportedException {
return (WorkStation) super.clone();
}
}
/**
* 浅复制
* 只能实现基本数据类型拷贝,对象,集合,容器等均为对象地址的引用
* 预期结果是,cloneA,cloneB都是克隆与Employee对象,设置三个不同地址,打印三个不同地址
* 实际结果是,三个地址相同,浅拷贝拷贝的是对象的引用,所以浅复制三个地址的值是一样的。
* <p>
* 深复制
* 两个条件,引用的对象也实现了Cloneable接口,并所复制的对象也重新set了引用对象的clone对象。
*
* @author zrj
* @date 2020/12/5
* @since V1.0
**/
public class PrototypeTest {
@Test
public void prototypeTest() throws CloneNotSupportedException {
Employee employee = new Employee( "艾伦", 18, new Address(), new WorkStation() );
employee.setAddressInfo( "1001", "西安" );
employee.setWorkStation( "1", "开发" );
System.out.println( "原对象:" + employee.toString() );
System.out.println( "-------------------浅复制---------------------" );
Employee cloneA = employee.clone();
cloneA.setAddressInfo( "1002", "上海" );
employee.setWorkStation( "2", "前端" );
System.out.println( "克隆对象A:" + cloneA.toString() );
Employee cloneB = employee.clone();
cloneA.setAddressInfo( "1003", "南京" );
employee.setWorkStation( "3", "测试" );
System.out.println( "克隆对象B:" + cloneB.toString() );
employee.showAddress();
cloneA.showAddress();
cloneB.showAddress();
System.out.println( "-------------------深复制---------------------" );
Employee cloneC = employee.deepClone();
cloneA.setAddressInfo( "1002", "北极" );
employee.setWorkStation( "2", "自然学家" );
System.out.println( "cloneC:" + cloneA.toString() );
Employee cloneD = employee.deepClone();
cloneA.setAddressInfo( "1003", "南极" );
employee.setWorkStation( "3", "物理学家" );
System.out.println( "cloneD:" + cloneB.toString() );
employee.showAddress();
cloneC.showAddress();
cloneD.showAddress();
}
}
输出结果:
原对象:Employee(name=艾伦, age=18, address=Address(addressCode=1001, addressName=西安), workStation=WorkStation(workCode=1, workStation=开发))
-------------------浅复制---------------------
克隆对象A:Employee(name=艾伦, age=18, address=Address(addressCode=1002, addressName=上海), workStation=WorkStation(workCode=2, workStation=前端))
克隆对象B:Employee(name=艾伦, age=18, address=Address(addressCode=1003, addressName=南京), workStation=WorkStation(workCode=3, workStation=测试))
------------------------------
姓名:艾伦,年龄:18,地址:南京,工作:测试
------------------------------
姓名:艾伦,年龄:18,地址:南京,工作:测试
------------------------------
姓名:艾伦,年龄:18,地址:南京,工作:测试
-------------------深复制---------------------
cloneC:Employee(name=艾伦, age=18, address=Address(addressCode=1002, addressName=北极), workStation=WorkStation(workCode=2, workStation=自然学家))
cloneD:Employee(name=艾伦, age=18, address=Address(addressCode=1003, addressName=南极), workStation=WorkStation(workCode=3, workStation=物理学家))
------------------------------
姓名:艾伦,年龄:18,地址:南极,工作:物理学家
------------------------------
姓名:艾伦,年龄:18,地址:南极,工作:测试
------------------------------
姓名:艾伦,年龄:18,地址:南极,工作:自然学家