原型模式是一种创建型设计模式。原型模式的思想是:通过对原型对象的克隆来获得新对象。
原型模式涉及3个角色:
- 抽象原型(Prototype):具体原型的抽象。如果是抽象类,则实现
Cloneable
接口并重写clone()
方法;如果是接口,则只给出规范,由实现Cloneable
接口并重写clone()
方法。 - 具体原型(Concrete Prototype):客户实际所需要的对象的类型。
- 客户(Client):提出创建对象的请求。
结构图:
代码实现:
抽象原型:
public abstract class Prototype implements Cloneable {
// 声明一些属性用作演示
protected int demoInt;
protected String demoString;
protected ArrayList<String> demoList;
// 浅克隆
public Prototype clone() {
Prototype cloneObj = null;
try {
cloneObj = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloneObj;
}
// 深克隆
@SuppressWarnings("unchecked")
public Prototype deepClone() {
Prototype cloneObj = null;
try {
cloneObj = (Prototype) super.clone();
cloneObj.setDemoList((ArrayList<String>) this.getDemoList().clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloneObj;
}
// Getters and Setters
// ......
}
具体原型:
public class ConcretePrototype extends Prototype {
public ConcretePrototype(){
this.demoInt = 111;
this.demoString = "AAA";
this.demoList = new ArrayList<>(2);
demoList.add("aaa111");
demoList.add("111aaa");
}
}
客户:
public class Client {
private Prototype prototype = new ConcretePrototype();
public Prototype getPrototype() {
return prototype.clone();
}
public Prototype getDeepPrototype() {
return prototype.deepClone();
}
// 测试
public static void main(String[] args) {
Client client = new Client();
Prototype a = client.getPrototype();
System.out.printf("%s, %s, %s\n", a.getDemoInt(), a.getDemoString(), a.getDemoList());
Prototype b = client.getDeepPrototype();
System.out.printf("%s, %s, %s\n", b.getDemoInt(), b.getDemoString(), b.getDemoList());
// 改变原型对象的内容看看效果
client.prototype.getDemoList().remove(0);
client.prototype.setDemoString("TEST");
System.out.printf("%s, %s, %s\n", a.getDemoInt(), a.getDemoString(), a.getDemoList());
System.out.printf("%s, %s, %s\n", b.getDemoInt(), b.getDemoString(), b.getDemoList());
}
}
运行结果:
111, AAA, [aaa111, 111aaa]
111, AAA, [aaa111, 111aaa]
111, AAA, [111aaa]
111, AAA, [aaa111, 111aaa]
java.lang.Object
类的clone()
方法实现的是浅克隆,除了基本数据类型和java.lang.String
类型的数据是值复制外,其余所有引用类型只复制一个引用,这个引用与被复制的对象的属性引用指向同一个对象。要实现深克隆,则必须人为指定对这些对象进行复制,前提是这些对象的类型实现了Cloneable
接口。当然,深克隆也可以通过序列化/反序列化的方法来实现,这种方式使得深克隆变得非常简单,但也需要注意一些细节问题,这里不展开。
上面的测试例子可以看到,a是浅克隆得到的对象,因此当原型对象引用类型的属性demoList
发生改变时,a对象的demoList
也跟着改变;而b是深克隆得到的对象,b对象的引用类型属性demoList
指向的则是新的List对象,所以原型对象改变,b并没有跟着改变。
由于绕过了构造器,原型模式能一定程度提高性能,尤其是当新建对象的开销比较大时。但其缺点也是明显的,在实现克隆方法时,需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,尤其当这个类引用不支持实例化的间接对象,或者可能会出现循环引用情况的时候。
原型模式很少单独使用,更多的时候是和其它设计模式一起出现。另外,这里提供一个可以使用原型设计模式的场景:缓存。缓存中的对象就相当于原型对象,当我们从缓存中取对象时,实际上取得的是这个对象的克隆。