原型模式就是用来生成同样的对象的!比如你的简历,你创建好一份简历之后,通过打印机不断的打印就能创建出多份相同的简历出来,这些简历内容都是一模一样的,用程序来表示的话,就是虽然对象不同(内存地址不同),但属性都是一模一样...
我们看看如果不用原型模式的话会是什么样子:
//一份简历
class Resume{
private String name ;//你的姓名
private String phone;//你的联系电话
private String email;//你的邮箱
private List<Work> works = new ArrayList<Work>() ;//你的多份工作经历
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public List<Work> getWorks() {return works;}
public void setWorks(List<Work> works) {this.works = works;}
//为什么要这样打印一下,要打印内存地址,我们接下来就知道了
public void show(){
String res = "当前对象地址:"+this+",引用属性works地址:"+works+"\r\n"
+"name:"+name+","
+"phone:"+phone+","
+"email:"+email+","
+"works:[\r\n";
for (Work work : works) {
res+=("companyName:"+work.getCompanyName()+",content:"+work.getContent()+"\r\n]");
}
System.out.println(res);
}
}
//工作经历
class Work{
private String companyName;//公司名称
private String content;//工作内容
public Work(String companyName, String content) {
this.companyName = companyName;
this.content = content;
}
public String getCompanyName() {return companyName;}
public void setCompanyName(String companyName) {this.companyName = companyName;}
public String getContent() {return content;}
public void setContent(String content) {this.content = content;}
}
public class Test {
public static void main(String[] args) {
//创建一份简历
Resume rOne = new Resume();
rOne.setName("段誉");
rOne.setPhone("18359612332");
rOne.setEmail("98785545@qq.com");
rOne.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系统架构..."));
//创建二份简历
Resume rTwo = new Resume();
rTwo.setName("段誉");
rTwo.setPhone("18359612332");
rTwo.setEmail("98785545@qq.com");
rTwo.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系统架构..."));
//创建三份简历
Resume rThree = new Resume();
rThree.setName("段誉");
rThree.setPhone("18359612332");
rThree.setEmail("98785545@qq.com");
rThree.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系统架构..."));
//还有N份...
//你该不会想直接写Resume rTwo = rOne;这种代码吧...拜托,这只是引用...
//好了,现在你的简历你要加上学历...专业...等等内容,怎么办?
//得每份简历都挨个去加上阿!后面无论是什么修改,你都要修改N次阿!如此,我们的原型模式就可以派上用场咯!
}
}
输出:***************************************************************
当前对象地址:Resume@961dff,引用属性works地址:[Work@18b81e3]
name:段誉,phone:18359612332,email:98785545@qq.com,works:[
companyName:阿里巴巴科技公司,content:智能助手系统架构...
]
当前对象地址:Resume@1fc6e42,引用属性works地址:[Work@1aaf0b3]
name:段誉,phone:18359612332,email:98785545@qq.com,works:[
companyName:阿里巴巴科技公司,content:智能助手系统架构...
]
当前对象地址:Resume@1a082e2,引用属性works地址:[Work@f0c85e]
name:段誉,phone:18359612332,email:98785545@qq.com,works:[
companyName:阿里巴巴科技公司,content:智能助手系统架构...
]
**********************************************************************
//一份简历,实现克隆接口
class Resume implements Cloneable{
private String name ;//你的姓名
private String phone;//你的联系电话
private String email;//你的邮箱
private List<Work> works = new ArrayList<Work>() ;//你的多份工作经历
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public List<Work> getWorks() {return works;}
public void setWorks(List<Work> works) {this.works = works;}
//覆盖次克隆方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void show(){
String res = "当前对象地址:"+this+",引用属性works地址:"+works+"\r\n"
+"name:"+name+","
+"phone:"+phone+","
+"email:"+email+","
+"works:[\r\n";
for (Work work : works) {
res+=("companyName:"+work.getCompanyName()+",content:"+work.getContent()+"\r\n]");
}
System.out.println(res);
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
//创建一份简历
Resume rOne = new Resume();
rOne.setName("段誉");
rOne.setPhone("18359612332");
rOne.setEmail("98785545@qq.com");
rOne.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系统架构..."));
//使用克隆创建第二份简历
Resume rTwo = (Resume) rOne.clone();
//使用克隆创建第三份简历
Resume rThree = (Resume) rOne.clone();
rOne.show();
rTwo.show();
rThree.show();
}
}
然而,我们来看一下Test类的输出,你会发现存在一个问题:
输出**********************************************************
当前对象地址:Resume@fcfa52,引用属性works地址:[Work@961dff]
name:段誉,phone:18359612332,email:98785545@qq.com,works:[
companyName:阿里巴巴科技公司,content:智能助手系统架构...
]
当前对象地址:Resume@18b81e3,引用属性works地址:[Work@961dff]
name:段誉,phone:18359612332,email:98785545@qq.com,works:[
companyName:阿里巴巴科技公司,content:智能助手系统架构...
]
当前对象地址:Resume@1fc6e42,引用属性works地址:[Work@961dff]
name:段誉,phone:18359612332,email:98785545@qq.com,works:[
companyName:阿里巴巴科技公司,content:智能助手系统架构...
]
**********************************************************
我们可以看到,虽然Resume是一个崭新的对象(内存地址不同),但它的属性并不是完全崭新的,仔细看它的works属性,这是一个引用属性,我们可以看到,它的内存地址还是一样的!这就存在问题了,在某些情况下,可能需要对引用属性进行操作,此时无论是操作rOne还是rTwo的works,实际上都是在操作同一个对象!那么这种还存在有同样引用的克隆方式,我们称之为浅克隆,与之相反的是深克隆,既任何属性都是崭新的!
要实现深克隆,需要我们改写clone方法,比如这样:
//一份简历,实现克隆接口
class Resume implements Cloneable{
//省略...
//覆盖次克隆方法
@Override
public Object clone() throws CloneNotSupportedException {
Resume resume = (Resume) super.clone();
//让works也构建一份新的
List<Work> newWorks = new ArrayList<Work>();
newWorks.addAll(this.works);
resume.works = newWorks;
return resume;
}
//省略
}
好了,原型模式就这些,它的实现主要依赖于JDK的Cloneable接口,需要注意的是它的浅克隆和深克隆!很简单吧!