原型模式定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
通用类图
具体实现
原型模式不是通过new关键字来产生一个对象,而是通过对象复制来实现的。Java提供了一个Cloneable接口来标示这个对象是可拷贝的,并且类重写了clone()方法,在JVM中才能被拷贝。当调用clone方法时构造函数不会被执行。Object类的clone方法的原理是从内存中(具体地说就是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块。
下面就来看看具体的代码实现:
接口Prototype继承了Cloneable接口:
package com.yrs.prototype;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 18:48 2018/7/22
* @Modified By:
*/
public interface Prototype extends Cloneable{
Prototype clone();
void setValue(String str);
Object getValue();
}
类ConcreteShallowPrototype 实现了Prototype接口:
package com.yrs.prototype;
import java.util.ArrayList;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 18:54 2018/7/22
* @Modified By:
*/
public class ConcreteShallowPrototype implements Prototype {
private ArrayList<String> arrayList = new ArrayList<String>();
public ConcreteShallowPrototype() {
System.out.println("shallow clone constructor execute");
}
@Override
public ConcreteShallowPrototype clone() {
ConcreteShallowPrototype prototype = null;
try {
//浅拷贝
prototype = (ConcreteShallowPrototype) super.clone();
} catch (CloneNotSupportedException e) {
//异常处理
e.printStackTrace();
}
return prototype;
}
//设置HashMap的值
public void setValue(String value) {
this.arrayList.add(value);
}
//取得arrayList的值
public ArrayList<String> getValue() {
return this.arrayList;
}
}
类ConcreteDeepPrototype 实现了Prototype接口:
package com.yrs.prototype;
import java.util.ArrayList;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 18:54 2018/7/22
* @Modified By:
*/
public class ConcreteDeepPrototype implements Prototype {
private ArrayList<String> arrayList = new ArrayList<String>();
public ConcreteDeepPrototype() {
System.out.println("deep clone constructor execute");
}
@Override
public ConcreteDeepPrototype clone() {
ConcreteDeepPrototype prototype = null;
try {
//深拷贝
prototype = (ConcreteDeepPrototype) super.clone();
prototype.arrayList = (ArrayList<String>) this.arrayList.clone();
} catch (CloneNotSupportedException e) {
//异常处理
e.printStackTrace();
}
return prototype;
}
//设置HashMap的值
public void setValue(String value) {
this.arrayList.add(value);
}
//取得arrayList的值
public ArrayList<String> getValue() {
return this.arrayList;
}
}
类Client:
package com.yrs.prototype;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 19:04 2018/7/22
* @Modified By:
*/
public class Client {
public static void main(String[] args) {
Prototype shallowPrototype = new ConcreteShallowPrototype();
shallowPrototype.setValue("yrs");
Prototype copyShallowPrototype = shallowPrototype.clone();
System.out.println(shallowPrototype.getValue());
System.out.println(copyShallowPrototype.getValue());
copyShallowPrototype.setValue("ByrsH");
System.out.println(shallowPrototype.getValue());
System.out.println(copyShallowPrototype.getValue());
System.out.println("**************************");
Prototype deepPrototype = new ConcreteDeepPrototype();
deepPrototype.setValue("yrs");
Prototype copyDeepPrototype = deepPrototype.clone();
System.out.println(deepPrototype.getValue());
System.out.println(copyDeepPrototype.getValue());
copyDeepPrototype.setValue("ByrsH");
System.out.println(deepPrototype.getValue());
System.out.println(copyDeepPrototype.getValue());
}
}
程序运行结果:
shallow clone constructor execute
[yrs]
[yrs]
[yrs, ByrsH]
[yrs, ByrsH]
**************************
deep clone constructor execute
[yrs]
[yrs]
[yrs]
[yrs, ByrsH]
由上面的程序运行结果可以看出执行clone()方法时,并没有执行类的构造函数。你同时也会发现两个类型的对象调用clone()后产生的对象,操作成员变量的结果是不同的。这就牵扯到浅拷贝和深拷贝的问题,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,但其他的原始类型比如int、long、char等都会被拷贝,String类型也会被拷贝,这种拷贝就叫做浅拷贝。是一种非常不安全的方式。ConcreteDeepPrototype 类实现了深拷贝,通过调用成员变量的clone方法来实现,两个对象之间的修改互不影响。还有一种方式可以实现深拷贝:通过二进制流来操作对象,然后实现对象的深拷贝。
二进制流实现深拷贝:
package com.yrs.prototype;
import java.io.*;
import java.util.ArrayList;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 9:43 2018/7/18
* @Modified By:
*/
public class DeepCopyByStreamPrototype implements Serializable {
private ArrayList<String> arrayList = new ArrayList<String>();
public DeepCopyByStreamPrototype deepCopy() throws IOException, ClassNotFoundException {
//将对象写入流中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//将对象从流中取出来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (DeepCopyByStreamPrototype) ois.readObject();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
DeepCopyByStreamPrototype prototype = new DeepCopyByStreamPrototype();
prototype.arrayList.add("yrs");
DeepCopyByStreamPrototype copyPrototype = prototype.deepCopy();
System.out.println(copyPrototype);
}
}
优点:
* 性能优良
* 逃避构造函数的约束:优点就是减少了约束,缺点也是减少了约束
应用场景:
* 资源优化场景:类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
* 性能和安全要求的场景:通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
* 一个对象多个修改者的场景:
参考:
《设计模式之禅 第二版》