定义
原型模式(Prototype):使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
结构
- Porototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是接口,甚至可以是具体实现类。
- ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
- Client(客户类):在客户类中,让一个原型对象克隆自身从而创建一个新的对象,只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Portotype编程,因此用户可以根据需要选择的具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
浅克隆与深克隆
- 浅克隆
在浅克隆中,如果原型对象的成员变量是值类型(如int、double、byte、boolean、char等基本数据类型),将复制一份给克隆对象;如果原型对象的成员变量是引用类型(如类、接口、数组等复杂数据类型),则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当原型对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。 - 深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一一份给克隆对象。简单来说,在深克隆中,除了对象本省被复制外,对象所包含的所有成员变量也将复制被复制。
代码
抽象原型
public abstract class Prototype {
public abstract Prototype clone();
}
public class ConcretePrototype extends Prototype {
private String attr;
@Override
public Prototype clone() {
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAttr(attr);
return prototype;
}
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
}
客户端调用
public class Client {
public static void main(String[] args) {
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAttr("name");
ConcretePrototype concretePrototype = (ConcretePrototype) prototype.clone();
System.out.println(concretePrototype.getAttr());
}
}
执行结果
name
Java中的clone()方法和Cloneable接口
public class ConcretePrototype1 implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅克隆
}
}
原型管理器
将多个原型对象储存在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合对象用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对象的原型对象来获取。
import java.util.Hashtable;
import java.util.Map;
public class PortotypeManager {
private Map<String, Prototype> prototypeMap = new Hashtable<>();
public PortotypeManager() {
prototypeMap.put("A", new ConcretePrototypeA());
prototypeMap.put("B", new ConcretePrototypeB());
}
public void add(String key, Prototype prototype) {
prototypeMap.put(key, prototype);
}
public Prototype get(String key) {
return prototypeMap.get(key).clone();
}
}
优/缺点与适用环境
- 优点
- 当创建新的对象实例较为复杂时,适用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高新实例的创建效率。
- 扩展性好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而降具体原型类写在配置文件中,增加或减少产品类对原有系统没有任何影响。
- 原型模式提供简化的创建结构,工厂方法模式常常需要一个产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制时通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
- 可以通过深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(例如恢复到某一历史状态),可辅助实现撤销操作。
- 缺点
- 需要为每一个原型类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时需要修改源代码,违背了开闭原则。
- 在实现深克隆的时候需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。
- 适用环境
- 创建对象的成本较大(例如初始化需要占用较长的时间、占用太多的CPU资源或者网络资源),新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量稍作修改。
- 系统需要保存对象的状态,而对象的状态变化很小。
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或者很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数来创建一个实例更加方便。