1.引入:
原型模式也是一种创建模式,采用将原对象作为原型,对这个原对象进行克隆,克隆出一个新对象。克隆就是从一个对象再创建另外一个可定制的对象并且不需知道任何创建的细节。
2.例子:
假如应聘者去面试,不可能只应聘一家公司,肯定会面试很多家公司,每家公司都需要把你的简历留下来和你的笔试题,作为选拔的标准。那么应聘者就需要准备多份简历,那么从设计模式的角度看,如果应聘者是小菜鸟,那么肯定就是不停的new简历对象。如果一份简历写错了,那么都需要修改。而如果你写完第一份简历,然后第二份,第三份。。。都复制第一份,如果第一份错了,就修改第一份,重新复制,岂不是很方便。这个所谓的复制就是克隆。如果克隆的新对象与原对象有关系那么就是浅复制,如果克隆的新对象与原对象毫无关系那么就是深复制。
3.核心:
3.1浅复制:
简历类中会有姓名和年龄,重要的是需要有项目经验,意思就是简历中关键字段就是这三个。如果想要更换简历那么就需要换这三个字段。需要把这三个字段暴露出来,浅复制实际就是搞Object类的clone()方法。克隆出一个新对象。
3.1.1.项目经验类:
//简历中的项目经验
public class ProjectExperience {
//简历中项目经验对应的项目名称
String projectName;
//简历中项目经验对应的项目技术
String projectTechnology;
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectTechnology() {
return projectTechnology;
}
public void setProjectTechnology(String projectTechnology) {
this.projectTechnology = projectTechnology;
}
}
解释:项目经验中提供了项目名称和项目技术,并且这两个字段不能是private,这个项目经验类是提供给简历类的,作为简历类的一部分,而项目名称和项目技术是对外暴露的,使不同应聘者可以克隆不同项目经验和不同项目技术的简历,所以字段为private的,需要把字段设置进去,所以要提供set方法。
3.1.2.简历类:
public class Resume implements Cloneable{
private String name;
private String age;
private ProjectExperience projectExperience;
public Resume(String name){
this.name=name;
projectExperience=new ProjectExperience();
}
public Resume(String name,String age){
this.name=name;
this.age=age;
projectExperience=new ProjectExperience();
}
public void setProjectExperience(String projectName,String projectTechnology) {
projectExperience.projectName=projectName;
projectExperience.projectTechnology=projectTechnology;
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
//显示简历内容
public void show(){
System.out.println("应聘者简历"+"\n姓名:"+name+"\n年龄:"+age+"\n项目名称:"+projectExperience.projectName+"\n项目技术:"+projectExperience.projectTechnology);
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
Object obj=this.clone();
return obj;
}
}
解释:提供了字段姓名和年龄,以及对应的构造方法。应聘者可以先初始化一个只有名字的简历,也可以初始化一个有名字和姓名的简历提供了它们的set方法便于修改简历。并且把项目经验类作为参数,并且提供了设置项目经验类的方法可以设置项目名称和项目技术。主要就是重写了clone方法,这个clone方法用于克隆这个简历类。
测试类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Resume resume1=new Resume("张三");
resume1.setAge("11");
resume1.setProjectExperience("聊天室", "javaweb");
resume1.show();
//如果克隆resume1之后产生的resume2,resume2如果没有设置值,那么值还是原来的resume1的值。
Resume resume2=(Resume) resume1.clone();
resume2.setName("李四");
resume2.setAge("9");
resume2.show();
}
}
虽然克隆resume1之后产生了resume2,如果resume2没有设置值,那么resume2对象的属性值就和resume1是一样。如果使用了resume2克隆resume1来的属性的set方法。虽然把进行的替换,但是有一点很清楚resume2的产生源于resume1对象。对于项目经验中字段项目名称和项目技术这种不同于name和age的是,把对象的引用如果也克隆来了的话,实际上是克隆的对象的引用,而不是把对象克隆来。对于name和age就是把值类型进行逐位克隆的。所以说浅复制就是把克隆对象和原对象保持关系。
测试结果:
应聘者简历
姓名:张三
年龄:11
项目名称:聊天室
项目技术:javaweb
应聘者简历
姓名:李四
年龄:9
项目名称:聊天室
项目技术:javaweb
3.2.深复制:
深复制就是克隆对象和原对象无关系,克隆对象都是重新创建的。
3.2.1.项目经验类:
//简历中的项目经验
public class ProjectExperience implements Cloneable{
//简历中项目经验对应的项目名称
String projectName;
//简历中项目经验对应的项目技术
String projectTechnology;
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectTechnology() {
return projectTechnology;
}
public void setProjectTechnology(String projectTechnology) {
this.projectTechnology = projectTechnology;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
由于是深复制,不再像是浅复制中把所有属性都暴露出来到原对象中,再通过原对象进行克隆出新对象。而深复制从对象的属性就开始克隆。由于项目经验类作为简历类的属性,所以要提供clone方法。
3.2.2.简历类:
public class Resume implements Cloneable{
private String name;
private String age;
private ProjectExperience projectExperience;
public Resume(String name){
this.name=name;
projectExperience=new ProjectExperience();
}
private Resume(ProjectExperience projectExperience) throws CloneNotSupportedException{
this.projectExperience=(ProjectExperience) projectExperience.clone();
}
public ProjectExperience getProjectExperience() {
return projectExperience;
}
public void setProjectExperience(String projectName,String projectTechnology) {
projectExperience.projectName=projectName;
projectExperience.projectTechnology=projectTechnology;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public void show(){
System.out.println("应聘者简历"+"\n姓名:"+name+"\n年龄:"+age+"\n项目名称:"+projectExperience.projectName+"\n项目技术:"+projectExperience.projectTechnology);
}
@Override
protected Object clone() throws CloneNotSupportedException {
Resume resume=new Resume(this.projectExperience);
resume.age=age;
resume.name=name;
return resume;
}
}
由于项目经验类是克隆到新对象中的,所以简历类需要提供一个传入项目经验类的方法,通过构造方法的方式传入。虽然最后测试时也是使用resume的clone的方法复制,但是深复制中的clone方法,已经把Resume中的每个属性的细节都复制到新克隆对象。所以
原对象和克隆对象已经失去了关系。
测试类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Resume resume1=new Resume("张三");
resume1.setAge("11");
resume1.setProjectExperience("聊天室", "javaweb");
resume1.show();
//如果克隆resume1之后产生的resume2,resume2如果没有设置值,那么值还是原来的resume1的值。
Resume resume2=(Resume) resume1.clone();
resume2.setName("李四");
resume2.setAge("9");
resume2.show();
}
}
测试结果:
应聘者简历
姓名:张三
年龄:11
项目名称:聊天室
项目技术:javaweb
应聘者简历
姓名:李四
年龄:9
项目名称:聊天室
项目技术:javaweb
----------------------------------------------------------------------------
4.总结:
浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
从整体看浅复制就是把需要复制的属性暴露出来,然后把对象整体clone过去。所以克隆对象和原对象是有关系,因为克隆对象你的细节都来源于人家原对象。
深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
从整体看深复制就是把原对象的每个属性进行了复制,然后对外直接提供了重写的clone 的方法,由于表面调用了重写的clone的方法,实际上已经把原对象的每个属性都克隆走了,所以克隆对象和原对象是无关系的,因为原对象你的属性细节都被人克隆走了,人家克隆对象的属性已经来源于人家自己克隆的属性了,这就是真的偷盗人家的东西吧还冠冕堂皇。。。