原型模型
原型模型的定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
原型模式的使用场景
- 类初始化需要消化非常多的资源,这个资源包括数据,硬件资源等,通过原型拷贝避免这些消耗。
- 通过 new 产生一个对象需要繁琐的数据准备或访问权限,这时可以使用原型模式。
- 一个对象需要提供给其他对象访问,而且各个调用者可能需要修改其值时,可以考虑使用原型模型拷贝多个对象供调用者使用,即保护拷贝。
原型模型的 UML 类图
角色介绍:
- Client:客户端用户
- Prototype: 抽象类或者接口,声明具备clone 能力
- ConcretePrototype: 具体的原型类。
原型模型的简单实现
下面用简单的文档拷贝为例演示一下简单的原型模式,当我们修改文档时,一般会将当前文档拷贝一份,然后在文档副本上进行修改,这个就是我们这个案例的基础。
/*
* 文档类型,扮演的是 ConcretePrototype 角色,而 cloneable是代表 protorype角色
* */
public class WordDocument implements Cloneable{
// 文本
private String mText;
// 图片名列表
private ArrayList<String> mImages = new ArrayList<String>();
public WordDocument(){
System.out.println("------------ WordDocument 构造函数——------");
}
protected WordDocument clone(){
try{
WordDocument document =(WordDocument) super.clone();
document.mText = this.mText;
document.mImages = this.mImages;
return document;
} catch (CloneNotSupportedException e) {
}
return null;
}
public String getmText(){
return mText;
}
public void setmText(String mText) {
this.mText = mText;
}
public List<String> getmImages() {
return mImages;
}
public void addImage(String img){
this.mImages.add(img);
}
/*
* 打印文档内容
* */
public void showDocument(){
System.out.println("------------ Word content start ——------");
System.out.println("text: "+ mText);
System.out.println("Images List:");
for (String imgName : mImages){
System.out.println("iamge name : "+ imgName);
}
System.out.println("------------ Word content end——------");
}
}
通过 WordDocument 类模拟了 word 文档中的基本元素,即图片和文字。 WordDocument 中的clone 方法用以实现对象克隆。
public class Client {
public static void main(String[] args) {
// 构建文档对象
WordDocument document = new WordDocument();
// 编辑文档,添加图片等
document.setmText("这是一篇文档");
document.addImage("图片1");
document.addImage("图片2");
document.addImage("图片3");
document.showDocument();
// 以原始文档为原型,拷贝一份副本
WordDocument document1 = document.clone();
document1.showDocument();
// 修改文档副本,不会影响原始文档
document1.setmText("这是修改的doc2文本");
document1.showDocument();
document.showDocument();
}
}
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L6TjGouh-1664930332652)(设计模式解析.assets/image-20220709101742205.png)]
浅拷贝和深拷贝
上面的原型拷贝实际上只是一个浅拷贝,这并不是将原始文档的所有字段都重新构造了一遍,而是副本文档的字段引用了原始文档的字段。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WtPoexoi-1664930332655)(设计模式解析.assets/image-20220709102243961.png)]
将main 函数的内容修改一下:
public static void main(String[] args) {
// 构建文档对象
WordDocument document = new WordDocument();
// 编辑文档,添加图片等
document.setmText("这是一篇文档");
document.addImage("图片1");
document.addImage("图片2");
document.addImage("图片3");
document.showDocument();
// 以原始文档为原型,拷贝一份副本
WordDocument document1 = document.clone();
document1.showDocument();
// 修改文档副本,不会影响原始文档
document1.setmText("这是修改的doc2文本");
document1.addImage("哈哈.jpg");
document1.showDocument();
document.showDocument();
}
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-quDRLsjn-1664930332656)(设计模式解析.assets/image-20220709103832887.png)]
从上面的运行结果,我们可以发现,最后两个文档的输出信息是一致的,这是因为备份文档只是引用原文档的地址,当备份文档增加一个图片时,原文档也会收到影响。这就是浅拷贝对象,如何解决这个问题呢,使用下面的代码实现深拷贝, clone 方法修改如下:
protected WordDocument clone(){
try{
WordDocument document =(WordDocument) super.clone();
document.mText = this.mText;
// 对 mImages 对象也调用 clone() 函数,进行深拷贝
document.mImages = (ArrayList<String>) this.mImages.clone();
return document;
} catch (CloneNotSupportedException e) {
}
return null;
}
原型模型的核心问题就是对原始对象进行拷贝,这个模式的使用过程中需要注意的就是:深、浅拷贝的问题,建议在使用该模式时尽量使用深拷贝,避免操作副本时影响原始对象的问题。
原型模型优缺点
优点:
原型模型是内存中二进制流的拷贝,要比直接 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模型可以更好地体现其优点。
缺点:
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,实际开发当中应该注意这个潜在的问题,优点就是减少了约束,缺点也是减少了约束,需要大家在实际开发时考虑。