原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。(引用自——《大话设计模式》)
原型类:
public abstract class Prototype {
private String id;
public Prototype(String id) {
this.id = id;
}
public String getId() {
return id;
}
public abstract Prototype clone();
}
具体类:
public class ConcretePrototypeOne extends Prototype {
public ConcretePrototypeOne(String id) {
super(id);
}
@Override
public Prototype clone() {
//创建当前对象的浅表副本
return this.clone();
}
}
客户端:
public class ResumeTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConcretePrototypeOne p1 = new ConcretePrototypeOne("I");
ConcretePrototypeOne c1 = (ConcretePrototypeOne) p1.clone();//克隆对象
L.e("复制的:" + c1.getId());
}
}
下面是一个具体的列子:
/**
* Created by zsf
* 原型模式
* WordDocument,文档类型,扮演的是ConcretePrototype角色,而cloneable是代表prototype角色
* 模拟了Word文档中的图片和文字元素,
*/
public class WordDocument implements Cloneable{
//文本
private String mText;
//图片名列表
private ArrayList<String> mImages = new ArrayList<>();
public WordDocument(){
L.e("--------------WordDocument构造函数");
}
/**
* 实现对象克隆,该方法不是Cloneable接口中的,而是Object中的方法。Cloneable也是一个标识接口,表明这个类的
* 对象是可以拷贝的。没有实现Cloneable接口却调用clone()方法会抛出异常
* @return
* @throws CloneNotSupportedException
*/
@Override
protected WordDocument clone() throws CloneNotSupportedException {
WordDocument doc = (WordDocument) super.clone();
doc.mText = this.mText;
doc.mImages = this.mImages;
return doc;
}
public String getText(){
return mText;
}
public void setText(String mText){
this.mText = mText;
}
public List<String> getImages(){
return mImages;
}
public void addImage(String img){
this.mImages.add(img);
}
/**
* 打印文档内容
*/
public void showDoucment(){
L.e("----------word Content Start------");
L.e("Text:" + mText);
L.e("Images List:");
for (String imgName : mImages){
L.e("image name :" + imgName);
}
L.e("------word Content End------");
}
}
客户端代码:
/**
* Created by zsf
*
* 注意!!通过clone拷贝对象时,并不会执行构造函数
*/
public class WordDocumentTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1.构建文档对象
WordDocument document = new WordDocument();
//2.编辑文档,添加图片等
document.setText("这是一篇文档");
document.addImage("图片1");
document.addImage("图片2");
document.addImage("图片3");
document.showDoucment();
try {
//以原始文档为原型,拷贝一份副本
WordDocument copyDocument = document.clone();
copyDocument.showDoucment();
//修改文档副本,不会影响原始文档
copyDocument.setText("这是修改过的拷贝文档");
copyDocument.showDoucment();
document.showDoucment();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
我们发现上面的例子并没有Protototype抽象类,因为我们实现了Cloneable接口,并且重写了里面的clone()方法。
打印的Log
03-31 16:58:16.034 27552-27552/? E/zsf: --------------WordDocument构造函数
03-31 16:58:16.034 27552-27552/? E/zsf: ----------word Content Start------
03-31 16:58:16.035 27552-27552/? E/zsf: Text:这是一篇文档
03-31 16:58:16.035 27552-27552/? E/zsf: Images List:
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片1
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片2
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片3
03-31 16:58:16.035 27552-27552/? E/zsf: ------word Content End------
03-31 16:58:16.035 27552-27552/? E/zsf: ----------word Content Start------
03-31 16:58:16.035 27552-27552/? E/zsf: Text:这是一篇文档
03-31 16:58:16.035 27552-27552/? E/zsf: Images List:
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片1
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片2
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片3
03-31 16:58:16.035 27552-27552/? E/zsf: ------word Content End------
03-31 16:58:16.035 27552-27552/? E/zsf: ----------word Content Start------
03-31 16:58:16.035 27552-27552/? E/zsf: Text:这是修改过的拷贝文档
03-31 16:58:16.035 27552-27552/? E/zsf: Images List:
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片1
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片2
03-31 16:58:16.035 27552-27552/? E/zsf: image name :图片3
03-31 16:58:16.035 27552-27552/? E/zsf: ------word Content End------
03-31 16:58:16.036 27552-27552/? E/zsf: ----------word Content Start------
03-31 16:58:16.036 27552-27552/? E/zsf: Text:这是一篇文档
03-31 16:58:16.036 27552-27552/? E/zsf: Images List:
03-31 16:58:16.036 27552-27552/? E/zsf: image name :图片1
03-31 16:58:16.036 27552-27552/? E/zsf: image name :图片2
03-31 16:58:16.036 27552-27552/? E/zsf: image name :图片3
03-31 16:58:16.036 27552-27552/? E/zsf: ------word Content End------
谈及原型模式,不得不说的一个知识点就是浅拷贝和深拷贝
上面原型模式的实现只是一个浅拷贝,又称影子拷贝。原理就是副本文档的字段引用原始文档的字段。
我们知道A引用B就是两个对象指向同一个地址,当修改A时B也会改变,B修改时A同样会改变。
如何解决上面的问题就是要使用深拷贝。
深拷贝,即在拷贝对象的同时,对于引用型的字段也要采用拷贝的形式,而不是单纯引用形式。clone方法修改如下:
/**
* 克隆对象
*/
@Override
protected WordDocument clone(){
try{
WordDocument doc = (WordDocument)super.clone();
doc.mText = this.mText;
//对mImages对象也调用clone()函数,进行深拷贝
doc.mImages = (ArrayList<String>)this.mImages.clone();
return doc;
}catch(Exception e){
}
return null;
}
上面中doc.mImages指向了this.mImages的一份拷贝,而不是this.mImages本身,这样在copyDocument中添加图片不会影响原Document。
原型模式是简单的一个模式,核心问题就是对原始对象进行拷贝,在使用该模式时需要注意的就是深浅拷贝的问题,建议直接使用深拷贝。