定义
原型模式(Prototype Pattern)是指原型实例指定创建对象的 种类,并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数。属于创建型模式。
使用场景
1、类初始化消耗资源较多;
2、new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等);
3、构造函数比较复杂。
4、循环体中生产大量对象时,可读性下降。
原型模式的应用也是比较广泛的,比如spring中的scope=“prototype”、JSON.parseObject()以及BeanUtils等。
原型模式的实现方法
1.通过简单工厂模式将getter、setter封装到某个方法中,代码如下:
public interface Prototype{ Prototype clone(); }
import java.util.List;
public class ConcretePrototype implements Prototype {
private int age;
private String name;
private List hobbies;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
@Override
public ConcretePrototype clone() {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setAge(this.age);
concretePrototype.setName(this.name);
concretePrototype.setHobbies(this.hobbies);
return concretePrototype;
}
2.通过实现JDK提供的Cloneable接口,实现快速复制 :
public class ConcretePrototype implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
//复制的业务逻辑
return null;
}
}
原型模式的优缺点
优点:性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多;可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:必须配备克隆(或者可拷贝)方法;对克隆复杂对象或对克隆出的对象进行复杂改造时,易带来风险;深拷贝、浅拷贝要运用得当
浅克隆与深克隆(拓展)
浅克隆是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象;深克隆不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。简而言之,对象A1中包含对B1的引用,B1中包含对C1的引用。如果通过浅拷贝A1得到A2,那么A2 中依然包含对B1的引用,B1中依然包含对C1的引用;而深拷贝A1得到A2,那么A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。
例如:
import java.util.ArrayList;
import java.util.List;
public class PrototypeTest {
public static void main(String[] args) {
ConcretePrototype prototype= new ConcretePrototype();
prototype.setAge(18);
prototype.setName("prototype");
List hobbies = new ArrayList<String>();
hobbies.add("书法");
hobbies.add("美术");
prototype.setHobbies(hobbies);
ConcretePrototype cloneType= concretePrototype .clone(concretePrototype);
cloneType.getHobbies().add("技术控");
System.out.println("原型对象:" + prototype);
System.out.println("克隆对象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println("原型对象的爱好:" + prototype.getHobbies());
System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
}
运行结果:
我们给复制后的克隆对象新增一项爱好,发现原型对象也发生了变化,这显然不符合我们的预期。因为我们希望克隆出来的对象应该和原型对象是两个独立的对象,不应该再有联系了。从测试结果看出hobbies的引用地址是相同的,意味着只是完整复制了值类型数据,没有复制引用对象。换言之,所有的引用对象仍然指向原来的对象。这样的话,如果我们修改任意一个对象中的属性值,prototype和cloneType的hobbies值都会改变。这就是我们常说的浅克隆。
那么,如何实现深克隆呢,我们可以通过IO流的方式来实现:
public Object deepClone(){
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ConcretePrototype copy =(ConcretePrototype)ois.readObject();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
此外,也可以通过JSON串的方式实现深克隆:
public ConcretePrototype deepClone(){
JSONObject jsonObject = JSONObject.fromObject(this);
Object o = JSONObject.toBean(jsonObject, ConcretePrototype.class);
return (ConcretePrototype)o;
}
利用上面的测试代码,调用deepClone方法,结果如下:
我们便得到了预期的结果!