一、概述
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。如果创建对象的成本比较大,比如对象中的数据是经过复杂计算才能得到,或者需要从RPC接口或者数据库等比较慢的IO中获取,这种情况我们就可以使用原型模式,从其他已有的对象中进行拷贝,而不是每次都创建新对象,进行一些耗时的操作.
二、结构
原型模式包含如下角色:
- 抽象原型类(Prototype):它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类也可以是接口.
- 具体原型类(ConcretePrototype):实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象.
- 客户类(Client):在客户类中,让一个原型对象克隆自身从而创建一个新的对象.由于客户类针对抽象原型类Prototype编程.因此用户可以根据需要选择具体原型类,系统具有较好的扩展性,增加或者替换具体原型类都比较方便.
三、实现
原型模式的克隆分为浅克隆和深克隆。
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java中的Object类中提供了 clone() 方法来实现浅克隆。Cloneable 接口是上面的类图中的抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类。
四、代码实现
1、浅克隆
package com.xu.demo.prototypePattern;
public class ConcretePrototype implements Cloneable {
public ConcretePrototype() {
System.out.println("具体的原型对象创建完成!");
}
@Override
protected ConcretePrototype clone() throws CloneNotSupportedException {
System.out.println("具体的原型对象复制成功!");
return (ConcretePrototype) super.clone();
}
}
package com.xu.demo.prototypePattern;
public class prototypePattern {
public static void main(String[] args) throws CloneNotSupportedException {
test1();
}
public static void test1() throws CloneNotSupportedException {
ConcretePrototype c1 = new ConcretePrototype();
ConcretePrototype c2 = c1.clone();
System.out.println("对象c1和c2是同一个对象?" + (c1 == c2));
}
}
结果:
2、深拷贝
package com.xu.demo.prototypePattern;
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person() {
}
public Person(String name) {
this.name = name;
}
}
package com.xu.demo.prototypePattern;
public class ConcretePrototype implements Cloneable {
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
void show(){
System.out.println("嫌疑人姓名: " +person.getName());
}
public ConcretePrototype() {
System.out.println("具体的原型对象创建完成!");
}
@Override
protected ConcretePrototype clone() throws CloneNotSupportedException {
System.out.println("具体的原型对象复制成功!");
return (ConcretePrototype) super.clone();
}
}
package com.xu.demo.prototypePattern;
public class prototypePattern {
public static void main(String[] args) throws CloneNotSupportedException {
test2();
}
public static void test2() throws CloneNotSupportedException {
ConcretePrototype c1 = new ConcretePrototype();
Person p1 = new Person();
c1.setPerson(p1);
//复制c1
ConcretePrototype c2 = c1.clone();
//获取复制对象c2中的Person对象
Person p2 = c2.getPerson();
p2.setName("峰哥");
//判断p1与p2是否是同一对象
System.out.println("p1和p2是同一个对象?" + (p1 == p2));
c1.show();
c2.show();
}
// public static void test1() throws CloneNotSupportedException {
// ConcretePrototype c1 = new ConcretePrototype();
// ConcretePrototype c2 = c1.clone();
//
// System.out.println("对象c1和c2是同一个对象?" + (c1 == c2));
// }
}
结果:
其实现在不推荐大家用Cloneable接口,实现比较麻烦,现在借助Apache Commons或者springframework可以直接实现:
- 浅克隆:
BeanUtils.cloneBean(Object obj);BeanUtils.copyProperties(S,T);
- 深克隆:
SerializationUtils.clone(T object);
BeanUtils是利用反射原理获得所有类可见的属性和方法,然后复制到target类。
SerializationUtils.clone()就是使用我们的前面讲的序列化实现深克隆,当然你要把要克隆的类实现Serialization接口。