1、定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
2、原型模式两种表现形式
2.1 简单形式
2.1.1 模式结构
原型模式简单形式由三部分组成:
- Client(客户):客户类提出创建对象的请求。
- Prototype(抽象原型):这是一个抽象角色,声明一个克隆自己的接口。
- ConcretePrototype(具体原型):被复制的对象,需要实现抽象的原型角色所要求的接口。
2.1.2 实例
2.1.2.1 抽象原型(Prototype)
public abstract class Prototype implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.1.2.2 具体原型(ConcretePrototype)
public class ConcretePrototype extends Prototype {
// 其他操作
}
2.1.2.3 客户端调用
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype = new ConcretePrototype();
ConcretePrototype concretePrototype = (ConcretePrototype)prototype.clone();
System.out.println("prototype.hashCode=" + prototype.hashCode());
System.out.println("concretePrototype.hashCode=" + concretePrototype.hashCode());
}
}
2.2 登记形式
2.2.1 模式结构
原型模式登记形式由四部分组成:
- Client(客户):客户类提出创建对象的请求。
- Prototype(抽象原型):这是一个抽象角色,声明一个克隆自己的接口。
- ConcretePrototype(具体原型):被复制的对象,需要实现抽象的原型角色所要求的接口。
- PrototypeManager(原型管理器):提供各种原型对象的创建和管理。
2.2.2 实例
2.2.2.1 抽象原型(Prototype)
public abstract class Prototype implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.2.2.2 具体原型(ConcretePrototype)
public class ConcretePrototype extends Prototype {
// 其他操作
}
2.2.2.3 原型管理器(PrototypeManager)
public class PrototypeManager {
private static Map<String, Prototype> map = new HashMap<>();
private PrototypeManager(){
}
public static void setPrototype(String name, prototype prototype) {
map.put(name, prototype);
}
public static void removePrototype(String name) {
map.remove(name);
}
public static Prototype getPrototype(String name) throws Exception {
Prototype prototype = map.get(name);
if (prototype == null) {
throw new IllegalArgumentException("no prototype found");
}
return prototype;
}
}
2.2.2.4 客户端调用
public class Client {
public static void main(String[] args) {
try {
Prototype prototype = new ConcretePrototype();
PrototypeManager.setPrototype("com.freedom.ConcretePrototype", prototype);
ConcretePrototype concretePrototype = (ConcretePrototype)PrototypeManager.getPrototype("com.freedom.ConcretePrototype").clone();
System.out.println("prototype.hashcode=" + prototype.hashCode());
System.out.println("concretePrototype.hashcode=" + concretePrototype.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 两种形式的比较
- 如果要创建的原型对象数据较少而且比较固定的话,可采用第一种形式。在这种情况下,原型对象的引用可以由客户端自己保存。
- 如果要创建的原型对象数据不固定的话,可以采用第二种形式。在这种情况下,客户端不保存对原型对象的引用,这个任务被交给原型管理器角色。在克隆一个对象之前,客户端可以查看管理员对象是否已用一个满足要求的原型对象。如果有,可以从原型管理器角色取得这个对象引用;然后没有,客户端就需要自行复制此原型对象。
3、优缺点
3.1 优点
- 创建新的对象比较复杂时,可以利用原型原型模式简化对象的创建过程,同时也能够提高效率。
- 不用重新初始化对象,而是动态地获得对象运行时的状态。
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码。
3.2 缺点
- 每一个类都必须要配备一个clone方法。配备clone方法需要对类的功能进行通盘考虑,这对于全新的类来说并不是很难,但是对于已有的类来说并不容易。
4、浅克隆和深克隆
4.1 定义
- Object类的clone()方法使用的是浅克隆。浅克隆对于要克隆的对象,会复制其基本数据类型和String类型的属性的值给新的对象,而对于引用类型,例如数组、集合、引用对象等,仅仅复制一份引用给新产生的对象,即新产生的对象和原始对象中的非基本数据类型的属性都指向的是同一个对象。
- 深克隆除了要克隆基本数据类型之外,还需要克隆引用类型的数据。深克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
- 深克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅克隆还是继续采用深克隆。因此,在采取深克隆时,需要决定多深才算深。此外,在深克隆的过程中,很可能会出现循环引用的问题,必须小心处理。
4.2 实例
4.2.1 被引用的类
public class DeepTarget implements Cloneable, Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName(String name) {
return name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
4.2.2 抽象原型
public abstract class Prototype implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
4.2.3 具体原型
public class ConcretePrototype extends Prototype implements Serializable {
public DeepTarget deepTarget;
// 浅克隆
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 深克隆,方式一,使用clone方法
public Object deepCloneOne() throws CloneNotSupportedException {
ConcretePrototype prototype = (ConcretePrototype) super.clone();
prototype.deepTarget = (DeepTarget)deepTarget.clone();
return prototype;
}
// 深克隆,方式二,通过对象的序列化实现(推荐)
public Object deepCloneTwo() 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 ois.readObject();
}
}
4.2.3 客户端调用
public class Client {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
ConcretePrototype prototype = new ConcretePrototype();
prototype.deepTarget = new DeepTarget();
prototype.deepTarget.setName("freedom");
// 浅克隆
ConcretePrototype concretePrototype = (ConcretePrototype) prototype.clone();
System.out.println("prototype.hashCode=" + prototype.hashCode() + "\t deepTarget.hashCode=" + prototype.deepTarget.hashCode());
System.out.println("concretePrototype.hashCode=" + concretePrototype.hashCode() + "\t deepTarget.hashCode=" + concretePrototype.deepTarget.hashCode());
System.out.println("------------------------------------------------------");
// 深克隆,方式一
ConcretePrototype concretePrototype1 = (ConcretePrototype) prototype.deepCloneOne();
System.out.println("prototype.hashCode=" + prototype.hashCode() + "\t deepTarget.hashCode=" + prototype.deepTarget.hashCode());
System.out.println("concretePrototype1.hashCode=" + concretePrototype1.hashCode() + "\t deepTarget.hashCode=" + concretePrototype1.deepTarget.hashCode());
System.out.println("------------------------------------------------------");
// 深克隆,方式二
ConcretePrototype concretePrototype2 = (ConcretePrototype) prototype.deepCloneTwo();
System.out.println("prototype.hashCode=" + prototype.hashCode() + "\t deepTarget.hashCode=" + prototype.deepTarget.hashCode());
System.out.println("concretePrototype2.hashCode=" + concretePrototype2.hashCode() + "\t deepTarget.hashCode=" + concretePrototype2.deepTarget.hashCode());
}
}