一 定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式也是一种创建型模式,不同的是,新对象的创建不是通过new来创建,而是在有一个原型对象的基础上通过克隆的方式,快速生成一个和原对象一样的实例。
二 使用场景
- 资源优化:当类的初始化需要消耗非常多的资源时,这个资源包括数据资源,硬件资源等。
- 性能和安全要求:通过new产生一个对象需要非常繁琐的数据准备和访问权限。
- 一个对象,多个修改者:一个对象需要提供给其它对象访问,而且各个调用者可能都要修改其值时。
三 模式结构
- Client:客户端用户
- Prototype:抽象类或者接口,声明具备clone能力
- ConcretePrototype:具体的原型类
四 实例
定义一个类,实现简历的功能,然后通过复制的方式,复制简历。
public class Resume implements Cloneable {
private String name;
private String birthday;
private String school;
/**
* 构造方法
* @param name
*/
public Resume(String name) {
this.name = name;
}
public void setPersonInfo(String birthday, String school) {
this.birthday = birthday;
this.school = school;
}
public void display(){
System.out.println("姓名:"+name);
System.out.println("生日:"+birthday+" 毕业院校:"+school);
}
/**
* 克隆该实例
* @return
*/
@Override
protected Object clone(){
Resume resume=null;
try {
resume= (Resume) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return resume;
}
}
客户端测试
Resume resume1=new Resume("Jackson");
resume1.setPersonInfo("1990-05-23","华中科技大学");
Resume resume2= (Resume) resume1.clone();
System.out.println("-------------------- resume1 ----------------------");
resume1.display();
System.out.println("-------------------- resume2 ----------------------");
resume2.display();
浅拷贝和深拷贝
运用原型模式,实际上只是一个浅拷贝,该拷贝并不是将原始文档上的所有字段都重新构造了一份,而是拷贝文档的字段对原始文档字段的引用。
我们在上面代码加一个字段,设置公司的网列表。
public class Resume implements Cloneable {
private String name;
private String birthday;
private String school;
private List<String> company=new ArrayList<>();
/**
* 添加公司
* @param company
*/
public void addCompany(String company){
this.company.add(company);
}
public void display(){
System.out.println("姓名:"+name);
System.out.println("生日:"+birthday+" 毕业院校:"+school);
for (String company:company){
System.out.println("company:"+company);
}
}
/**
* 克隆该实例
* @return
*/
@Override
protected Object clone(){
Resume resume=null;
try {
resume= (Resume) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return resume;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public List<String> getCompany() {
return company;
}
public void setCompany(List<String> company) {
this.company = company;
}
}
客户端代码如下
Resume resume1=new Resume();
resume1.setName("Jackson");
resume1.setBirthday("1990-05-23");
resume1.setSchool("华中科技大学");
resume1.addCompany("腾讯");
resume1.addCompany("阿里巴巴");
resume1.addCompany("百度");
Resume resume2= (Resume) resume1.clone();
resume2.setName("Lucy");
resume2.setBirthday("1992-05-17");
resume2.setSchool("华东师范大学");
resume2.addCompany("小米");
System.out.println("-------------------- resume1 ----------------------");
resume1.display();
System.out.println("-------------------- resume2 ----------------------");
resume2.display();
我们在resume2里添加了小米,如果是深拷贝,resume1里应该不会有小米。
运行代码结果如下:
从结果可以看到,resume1里出现了小米,说明修改了拷贝,原数据也修改了,对于List<String> company,拷贝数据只是原始数据的引用。
深拷贝
修改clone()代码如下,对浅拷贝的ArrayList<String> company也调用clone()方法。
/**
* 克隆该实例
* @return
*/
@Override
protected Object clone(){
Resume resume=null;
try {
resume= (Resume) super.clone();
// 对List<String> company对象也调用clone()
resume.company=(ArrayList<String>)this.company.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return resume;
}
运行结果如下:
可以看到,resume1中已经没有了小米。
原型模式比较简单,比直接new一个对象要方便多了,特别是要在一个循环体中产生大量对象时。
但是,一定要注意,在拷贝过程中,构造方法是不会执行的。
网友评论