模式定义
用原型实例指定创建对象的种类,并且通过拷贝这个原型创建新的对象。
Prototype模式允许通过一个对象创建另外一个对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
术语
浅复制/浅拷贝, shallow copy
被复制对象的所有变量都含有与原来的对象相同的值(仅对于简单的值类型数据),而所有的对其他对象的引用都仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。深复制/深拷贝,deep copy
被复制对象的所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把重复的对象所引用的对象都复制一遍,而这种对被引用到的对象的复制叫做间接复制。
模式角色
原型模式包含如下角色:
- 抽象原型(abstract prototype):java中所有类都继承自Object,且Object实现了clone()方法,所以所有的对象都可以通过实现clone()接口实现具体原型;
- 具体原型类(concrete prototype):具体原型类如果实现了clone()方法,需要实现clonable接口,否则会抛出CloneNotSupported异常;
- 客户类(client):用来测试模式;
clone方法需要满足的几个原则:
- x.clone() != x,两个对象不是一个;
- x.clone().getClass() == x.getClass(),两个对象的class是同一个;
- x.clone().equals(x),两个应该满足equals()方法条件;
注意:
Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。幸运的是java提供的大部分的容器类都实现了Cloneable接口。所以实现深拷贝并不是特别困难。
接着我们看个简单的例子:
浅拷贝:
public class ConcretePrototypeShallowCopy implements Cloneable {
public List list = new ArrayList();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
深拷贝:
public class ConcretePrototypeDeepCopy implements Cloneable {
public List list = new ArrayList();
@Override
protected Object clone() throws CloneNotSupportedException {
ConcretePrototypeDeepCopy c = (ConcretePrototypeDeepCopy) super.clone();
c.list = (List) ((ArrayList)c.list).clone();
return c;
}
}
Client:
public class ConsoleTest {
public static void main(String[] args) throws CloneNotSupportedException {
ConcretePrototypeShallowCopy s1 = new ConcretePrototypeShallowCopy();
ConcretePrototypeShallowCopy s2 = (ConcretePrototypeShallowCopy) s1.clone();
ConcretePrototypeDeepCopy d1 = new ConcretePrototypeDeepCopy();
ConcretePrototypeDeepCopy d2 = (ConcretePrototypeDeepCopy) d1.clone();
}
}