原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。
原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。
代码实现:
原型基类:
package com.demo.prototype.prototype;
public class Prototype implements Cloneable {
public Prototype() {
System.out.println("Prototype is constructed.");
}
public void show() {
System.out.println("This is prototype.");
};
@Override
public Prototype clone() {
try {
return (Prototype)super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
原型类:
package com.demo.prototype.prototype;
public class ConcretePrototype extends Prototype {
@Override
public void show() {
System.out.println("This is a concrete prototype.");
}
}
客户类:
package com.demo.prototype.client;
import com.demo.prototype.prototype.Prototype;
public class Client {
private Prototype prototype;
public Client(Prototype prototype) {
this.prototype = prototype;
}
public Prototype createPrototype() {
return (Prototype) prototype.clone();
}
}
测试代码:
package com.demo.prototype;
import com.demo.prototype.client.Client;
import com.demo.prototype.prototype.ConcretePrototype;
import com.demo.prototype.prototype.Prototype;
public class MainApp {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype();
Prototype tempPrototype = null;
Client client = new Client(prototype);
for (int i = 0; i < 3; i++) {
tempPrototype = client.createPrototype();
tempPrototype.show();
}
}
}
结果输出:
Prototype is constructed.
This is a concrete prototype.
This is a concrete prototype.
This is a concrete prototype.
从结果输出可以看到,原型只被构造一次。
但是这里需要注意深拷贝与浅拷贝的问题,Java中,Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
实现深拷贝可以在重写clone方法中,对数组、容器对象、引用对象等自己进行拷贝。
还可以使用一个技巧,就是利用序列化的机制来实现,通过将对象序列化到输出流中,然后将其读回,这样产生的新对象是对现有对象的一个深拷贝。
代码实现:
package com.demo.deepcopy.bean;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public abstract class SerialCloneable implements Cloneable, Serializable {
private static final long serialVersionUID = 4751631810037492944L;
@Override
public Object clone() {
try {
/*
** 将对象序列化到一个输出流中
*/
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(bout);
output.writeObject(this);
output.close();
/*
** 将流中的数据读回,得到一个深拷贝的副本
*/
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream input = new ObjectInputStream(bin);
Object result = input.readObject();
input.close();
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
package com.demo.deepcopy.bean;
import java.util.Date;
public class DeepCopyBean extends SerialCloneable {
private static final long serialVersionUID = -5415862716339876656L;
private int primitiveField;
private Date objectField;
public int getPrimitiveField() {
return primitiveField;
}
public void setPrimitiveField(int primitiveField) {
this.primitiveField = primitiveField;
}
public Date getObjectField() {
return objectField;
}
public void setObjectField(Date objectField) {
this.objectField = objectField;
}
public DeepCopyBean(int primitiveField, Date objectField) {
setPrimitiveField(primitiveField);
setObjectField(objectField);
}
@Override
public String toString() {
return String.format("PrimitiveFieldValue : %d\nObjectFieldValue : %s",
primitiveField, objectField.toString());
}
}
测试代码:
package com.demo.deapcopy;
import java.util.Date;
import java.util.GregorianCalendar;
import com.demo.deepcopy.bean.DeepCopyBean;
public class MainApp {
public static void main(String[] args) {
DeepCopyBean bean = new DeepCopyBean(5, new Date());
DeepCopyBean clonedBean = (DeepCopyBean) bean.clone();
System.out.println(bean);
System.out.println();
System.out.println(clonedBean);
clonedBean.setObjectField(new GregorianCalendar(2008, 7, 8).getTime());
System.out.println("-----------------------------------------------");
System.out.println(bean);
System.out.println();
System.out.println(clonedBean);
}
}
结果输出:
PrimitiveFieldValue : 5
ObjectFieldValue : Fri Mar 22 13:03:23 CST 2013
PrimitiveFieldValue : 5
ObjectFieldValue : Fri Mar 22 13:03:23 CST 2013
-----------------------------------------------
PrimitiveFieldValue : 5
ObjectFieldValue : Fri Mar 22 13:03:23 CST 2013
PrimitiveFieldValue : 5
ObjectFieldValue : Fri Aug 08 00:00:00 CST 2008
可以看到修改了clonedBean的objectField属性并不影响bean的属性。
使用原型模式的适用场合:
1) 产生对象过程比较复杂,初始化需要许多资源时;
2) 希望框架原型和产生对象分开时;
3) 同一个对象可能会供其他调用者使用访问时。