1、定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
2、UML类图
3、角色
-
Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
-
ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
-
Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
原型模式的核心在于如何实现克隆方法。
4、代码实现
因为克隆是在是太常用了,所以java提供了Cloneable
接口,其中就是唯一的一个方法clone()
,这样你就只需要实现这个接口就可以完成原型模式了。
4.1、浅复制
clone()
方法是这样,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则赋值引用但不复制引用的对象:因此,原始对象及其复用引用同一对象。
就是说如果你“简历类中有对象引用,那么引用的对象数据是不会被克隆过来的”。
“浅复制”:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。但我们可能更需要这样的一种需求,把要复制的对象所引用的对象都复制一遍。
WorkExperience
package com.bing.Resume06;
public class WorkExperience {
private String workDate;
private String company;
public String getWorkDate() {
return workDate;
}
public void setWorkDate(String workDate) {
this.workDate = workDate;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
Resume
package com.bing.Resume06;
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
private WorkExperience work;
public Resume(String name) {
this.name = name;
// 在简历实例化的同时,实例化“工作经历”
work = new WorkExperience();
}
// 设置个人信息
public void setPersonalInfo(String sex, String age) {
this.sex = sex;
this.age = age;
}
//设置个人经历
public void setWorkExperience(String workDate, String company) {
work.setWorkDate(workDate);
work.setCompany(company);
}
// 显示
public void Display(){
System.out.println(name + " " + sex + " "+age);
System.out.println(work.getWorkDate()+ " " + work.getCompany());
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试
package com.bing.Resume06;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Resume a = new Resume("张三");
a.setPersonalInfo("男","39");
a.setWorkExperience("2321-2-43","AA公司");
Resume b = (Resume) a.clone();
b.setWorkExperience("2321-2-43","BB公司");
Resume c = (Resume) a.clone();
c.setPersonalInfo("男","23342");
c.setWorkExperience("23321-2-443","DG公司");
a.Display();
b.Display();
c.Display();
}
}
测试结果:
这里我们明显发现,值类型有用,而对于引用类型,就只复制了引用,对引用的对象只是指向了原来的对象。
4.2、深复制实现
深刻复制就是把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
WorkExperience
package com.bing.Resume06;
public class WorkExperience implements Cloneable {
private String workDate;
private String company;
public String getWorkDate() {
return workDate;
}
public void setWorkDate(String workDate) {
this.workDate = workDate;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
// “工作经历“类实现克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Resume
package com.bing.Resume06;
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
private WorkExperience work;
public Resume(String name) {
this.name = name;
// 在简历实例化的同时,实例化“工作经历”
work = new WorkExperience();
}
//通过clone方法调用的私有构造函数,以便克隆“工作经历”的数据
public Resume(WorkExperience work) throws CloneNotSupportedException {
this.work = (WorkExperience) work.clone();
}
// 设置个人信息
public void setPersonalInfo(String sex, String age) {
this.sex = sex;
this.age = age;
}
//设置个人经历
public void setWorkExperience(String workDate, String company) {
work.setWorkDate(workDate);
work.setCompany(company);
}
// 显示
public void Display(){
System.out.println(name + " " + sex + " "+age);
System.out.println(work.getWorkDate()+ " " + work.getCompany());
}
// 调用私有的构造方法,让“工作经历”克隆完成,然后再给这个“简历”对象的相关字段赋值,
// 最终返回一个深复制的简历对象
@Override
protected Object clone() throws CloneNotSupportedException {
Resume obj = new Resume(this.work);
obj.name = this.name;
obj.age = this.age;
obj.sex = this.sex;
return obj;
}
}
测试
package com.bing.Resume06;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Resume a = new Resume("张三");
a.setPersonalInfo("男","39");
a.setWorkExperience("2321-2-43","AA公司");
Resume b = (Resume) a.clone();
b.setWorkExperience("2321-2-43","BB公司");
Resume c = (Resume) a.clone();
c.setPersonalInfo("男","23342");
c.setWorkExperience("23321-2-443","DG公司");
a.Display();
b.Display();
c.Display();
}
}
运行结果