作为程序员的我们,面对时刻需求改变的情况,总会有懒惰的时候,有时候会使用拷贝代码的方式来完成需求,明明知道后果有时是很槽糕的,但是还是去做,可我们是否有一种可以解决在拷贝的方式下减少槽糕的事情发生的模式呢,答案是肯定的,有,那就是我们的原型模式,原型模式的定义是:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。这是官方的说法,接下来用案例说明。就例如我们在面试的时候常常要复制简历,或者写不同的简历,但是这些简历之间只有少些地方不同,我们使用程序来实现以下这个需求。
第一步:创建一个简历类,属性有姓名,年龄,性别,工作经验等。
//简历
public class Resume{
private String name; //姓名
private int age;//年龄
private String sex;//性别
public Resume(String name) {
this.name = name;
}
//设置个人信息
public void setPersonInfo(int age, String sex) {
this.age = age;
this.sex = sex;
}
//设置工作经验
public void setWorkExperience(String company, String timeArea) {
this.timeArea = timeArea;
this.company = company;
}
//显示简历信息
public void show() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.sex);
System.out.println("工作经验:在时间段" + this.timeArea + "内,就职于" + this.company + "公司");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
第二步:测试,创建三份相同的简历,然后显示简历的信息。
public class Test1 {
public static void main(String[] args) {
Resume r1 = new Resume("张三");
r1.setPersonInfo(22, "男");
r1.setWorkExperience("华为", "2015-09-12~2015-10-25");
r1.show();
Resume r2 = new Resume("张三");
r2.setPersonInfo(22, "男");
r2.setWorkExperience("华为", "2015-09-12~2015-10-25");
r2.show();
Resume r3 = new Resume("张三");
r3.setPersonInfo(22, "男");
r3.setWorkExperience("华为", "2015-09-12~2015-10-25");
r3.show();
}
}
第三步:认真一看,我们的客户端写了很多重复的代码,我们可以这样子改装一下,把r1对象的引用复制给r2和r3对象,但是这样子做是有一定风险的,例如在改变r1对象内容的时候,其他对象会受到无辜的牵连,这并不是我们想要的结果。
public class Test2 {
public static void main(String[] args) {
Resume r1 = new Resume("张三");
r1.setPersonInfo(22, "男");
r1.setWorkExperience("华为", "2015-09-12~2015-10-25");
Resume r2 = r1;
Resume r3 = r1;
//如改变r1对象的内容,则r2,r3也会受到牵连
//r2.setAge(25);
//r1.setAge(30);
r1.show();
r2.show();
r3.show();
}
}
第四步:如是我们可以使用原型模式来解决这个问题,通过拷贝的方式,让简历类实现接口Cloneable,并且重写Object的clone()方法。
//简历
public class Resume implements Cloneable{
private String name; //姓名
private int age;//年龄
private String sex;//性别
public Resume(String name) {
this.name = name;
}
//设置个人信息
public void setPersonInfo(int age, String sex) {
this.age = age;
this.sex = sex;
}
//设置工作经验
public void setWorkExperience(String company, String timeArea) {
this.timeArea = timeArea;
this.company = company;
}
//显示简历信息
public void show() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.sex);
System.out.println("工作经验:在时间段" + this.timeArea + "内,就职于" + this.company + "公司");
}
//克隆,实现Cloneable接口,覆盖其clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
第五步:再来测试一下,此时我们的测试类需要修改如下,现在
改变r1对象内容的时候,其他对象不再会受到无辜的牵连,这就是我们想要的结果。
public class Test3 {
public static void main(String[] args) {
Resume r1 = new Resume("张三");
r1.setPersonInfo(22, "男");
r1.setWorkExperience("华为", "2015-09-12~2015-10-25");
Resume r2 = null;
Resume r3 = null;
try {
r2 = (Resume)r1.clone(); //克隆,隐藏对象创建的细节,提高性能, 不重复初始化
r3 = (Resume)r1.clone(); //只执行一次构造方法
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//如改变r1对象的内容,r2,r3也不会受到牵连
r2.setAge(25);
r1.setAge(30);
r1.show();
r2.show();
r3.show();
}
}
第六步:总结如下,原型模式有很多优点:1.克隆隐藏了对象创建的细节。2.不重复初始化,只执行一次构造方法,提高了性能。
拷贝的方式种类有:浅复制和深复制,之前我们使用的就是浅复制,就是普通的值对象的拷贝,还没有涉及到对象的拷贝,也就是说我们需要拷贝的对象简历类里面没有其他对象,String对象比较特别,除外。接下来就说一下如何拷贝对象的时候把对象内部的属性对象也进行拷贝。
第七步:我们把工作经验抽取为一个类,也让这个类实现接口Cloneable和重写clone()方法。
public class Experience implements Cloneable{
public String timeArea; //工作时间段
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String company; //公司
public String getTimeArea() {
return timeArea;
}
public void setTimeArea(String timeArea) {
this.timeArea = timeArea;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
第八步:简历类进行如下的修改,在简历类里面有一个工作经验的属性对象,在创建实例化简历类的时候把工作经验类创建,同时修改简历类中的clone()方法,结合注释进行慢慢体会。
//简历
public class Resume implements Cloneable{
private String name; //姓名
private int age;//年龄
private String sex;//性别
private Experience workExperience = null;
public Resume(String name) {
this.name = name;
workExperience = new Experience();
}
//提供Clone方法调用的私有构造函数,以便克隆“工作经验”的数据
public Resume(Experience workExperience) throws CloneNotSupportedException {
this.workExperience = (Experience) workExperience.clone();
}
//设置个人信息
public void setPersonInfo(int age, String sex) {
this.age = age;
this.sex = sex;
}
//设置工作经验
public void setWorkExperience(String company, String timeArea) {
workExperience.timeArea = timeArea;
workExperience.company = company;
}
//显示简历信息
public void show() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age + ",性别:" + this.sex);
System.out.println("工作经验:在时间段" + workExperience.timeArea + "内,就职于" + workExperience.company + "公司");
}
@Override
protected Object clone() throws CloneNotSupportedException {
//调用私有的构造方法,让工作经验完成克隆,然后在赋值普通的字段,
//最有返回一个深复制的简历对象
Resume re = new Resume(this.workExperience);
re.age = this.age;
re.name = this.name;
re.sex = this.sex;
return re;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
第九步:测试一下,这次采用深度复制,会把简历对象内部的属性对象来拷贝过来,赶紧运行来见证奇迹吧!哈哈!
public class Test5 {
public static void main(String[] args) {
Resume r1 = new Resume("张三");
r1.setPersonInfo(22, "男");
r1.setWorkExperience("华为", "2015-09-12~2015-10-25");
Resume r2 = null;
Resume r3 = null;
try {
//采用深度复制
r2 = (Resume)r1.clone();
r3 = (Resume)r1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
r2.setWorkExperience("华为", "2015-10-12~2015-10-25");
r3.setWorkExperience("华为", "2015-11-12~2015-10-25");
r1.show();
r2.show();
r3.show();
}
}