简介:
- 原型模式是一个创建型的模式。
- 通过给出一个原型对象来指明所创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。
- 原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例,这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。
使用场景
- 原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。
- 通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
- 个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
原型模式的参与角色
- 客户角色:客户类提出创建对象的请求。
- 抽象原型角色:这是一个抽象角色,通常由一个java接口或抽象类实现,具体原型都需要实现该接口。
- 具体原型角色:客户端所需要的被复制的对象。
实例演示
/**
*
* @ Description:Cloneable 扮演抽象原型角色 Book 扮演具体原型角色,当然也可以设成抽象原型角色,由其他具体对象来实现
* @Version: $version$
*/
public class Book implements Cloneable {
private String title;
private ArrayList<String> images = new ArrayList<String>();
@Override
protected Object clone() throws CloneNotSupportedException {
Book book = (Book) super.clone();
return book;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public ArrayList<String> getImages() {
return images;
}
public void setImages(String images) {
this.images.add(images);
}
public void showBook() {
System.out.println("----------------------Start----------------------");
System.out.println("title:" + title);
for (String img : images) {
System.out.println("image name:" + img);
}
System.out.println("----------------------End----------------------");
}
}
客户角色
public class Client {
public static void main(String[] args) {
// 1.构建书本对象
Book book1 = new Book();
// 2.编辑书本,添加图片
book1.setTitle("书1");
book1.addImage("图1");
book1.showBook();
// 以原型文档为原型,拷贝一份副本
Book book2 = (Book) book1.clone();
book2.showBook();
// 修改图书副本,不会影响原始书本
book2.setTitle("书2");
book2.showBook();
// 再次打印原始书本
book1.showBook();
}
}
输出
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书2
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
备注:
book2是book的一份拷贝,它和book1的内容是一样的。而book2修改了“标题”-字符串(基本的数据类型),并不影响book1的标题,这就保证了book1的安全性。
浅拷贝引用
public class Client {
public static void main(String[] args) {
// 1.构建书本对象
Book book1 = new Book();
// 2.编辑书本,添加图片
book1.setTitle("书1");
book1.addImage("图1");
book1.showBook();
// 以原型文档为原型,拷贝一份副本
Book book2 = (Book) book1.clone();
book2.showBook();
// 修改图书副本,不会影响原始书本
book2.setTitle("书2");
book2.addImage("图2");
book2.showBook();
// 再次打印原始书本
book1.showBook();
}
}
输出
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书2
image name:图1
image name:图2
----------------------End----------------------
----------------------Start----------------------
title:书1
image name:图1
image name:图2
----------------------End----------------------
备注
最后两个书本内容输出是一致的。引用类型的新对象book2的image只是单纯指向了this.image引用,并没有重新构造一个image对象,然后将原始书本的图片添加到新的image对象中,这样导致book2中的image与原始书本中的是同一个对象。因此,修改其中一个书本的图片,另一个书本也会受到影响。
如何解决?因为Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、集合、容器对象、引用对象等都不会拷贝;所以采用深拷贝。
深拷贝应用
/**
* 重写拷贝方法
*/
@Override
protected Book clone() {
try {
Book book = (Book) super.clone();
// 对image对象也调用clone()函数,进行拷贝
book.image = (ArrayList<String>) this.image.clone();
return book;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
输出
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
----------------------Start----------------------
title:书2
image name:图1
image name:图2
----------------------End----------------------
----------------------Start----------------------
title:书1
image name:图1
----------------------End----------------------
备注
将book.image指向this.image的一份拷贝,而不是this.image本身,实现了完全的拷贝,这样再互不影响。