原型模式的概念
原型模式是一种通过复制原对象的方式来创建新对象的创建型设计模式,这种创建对象的方式,避免了重复执行初始化对象的过程,并且如果一个对象初始化的过程成本过高,通过原型模式的方式来新建对象会节约对象的创建成本。原型模式是基于拷贝实现的,拷贝又分为深拷贝和浅拷贝;二者的区别是,前者在拷贝对象的时候,会将对象内部的引用类型的属性指向一块新的内存地址,但是后者的内部引用属性依然会指向和被拷贝对象中引用属性的同一块内存地址。
也就是说,深拷贝既复制了对象中的值类型属性,又复制了对象中的引用类型属性,但是浅拷贝实际上只复制了对象中的值类型属性,对于引用类型的属性仅仅是让其指向了原属性的内存地址。
原型模式通常包含以下几个角色
- 抽象原型类 定义了用来拷贝自身实例的抽象方法
- 具体原型类 实现了用来拷贝自身实例的具体方法
- 通过具体原型创建新对象的客户端 用来调用具体原型对象来创建新对象的角色
代码示例及说明
用一个生产汉堡的例子来说明原型模式是如何创建对象的,因为Java中存在包含拷贝方法的抽象类Cloneable,所以直接创建具体的原型类实现Cloneable的clone()方法就好
创建一个具体原型类
/**
* 抽象汉堡原型类
*
*/
public class HamBurgPrototype implements Cloneable{
private long price;//汉堡的价格
private String name;//汉堡的名称
private Translate translate;//折扣
@Override
public HamBurgPrototype clone() throws CloneNotSupportedException {
return (HamBurgPrototype)super.clone();
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Translate getTranslate() {
return translate;
}
public void setTranslate(Translate translate) {
this.translate = translate;
}
}
上面的类中包含三个属性,一个是值类型的price,一个是引用类型的name,还有一个是我们自己创建的引用类型,用来表示汉堡的折扣信息,clone()方法是完成对象拷贝的核心。
创建原型模式的客户端类实现对象的创建
/**
* 原型模式客户端类
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
HamBurgPrototype hamBurg = new HamBurgPrototype();
hamBurg.setName("香辣鸡腿堡");
hamBurg.setPrice(19);
hamBurg.setTranslate(new Translate());
HamBurgPrototype hamBurg2 = hamBurg.clone();
System.out.println(hamBurg.getName()==hamBurg2.getName());
System.out.println(hamBurg.getPrice()==hamBurg2.getPrice());
System.out.println(hamBurg.getTranslate()==hamBurg2.getTranslate());
}
}
上面的客户端main方法中,先创建一个具体原型类的对象,并通过其内部属性的set方法将属性赋值,随后调用hamBurg.clone()方法通过克隆创建新的对象。至此就通过原型模式成功的创建了一个基于原型对象的新对象。上面的代码运行的结果:
在
java中,引用类型用==号比较引用,比较的是引用的内存地址是不是相同,上面的代码hamBurg实例中的translate和hamBurg2的translate属性引用地址是相同的,因为使用的是浅拷贝创建对象,不会为引用类型的属性开辟新的内存地址,所以translate属性的比较结果是true。
使用深拷贝复制原型对象
如果想要对原型类进行深拷贝,首先要在原类型中的引用类型中也实现Cloneable接口的clone方法,比如上面HamBurgPrototype中的translate属性,先让Translate类实现clone方法
public class Translate implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return (Translate)super.clone();
}
}
紧接着在HamBurgPrototype原型类中的clone方法中,调用translate属性的clone方法,完成原型类的深拷贝,修改后的clone方法如下
@Override
public HamBurgPrototype clone() throws CloneNotSupportedException {
HamBurgPrototype hamBurg = (HamBurgPrototype)super.clone();
hamBurg.translate = (Translate) this.translate.clone();
return hamBurg;
}
再次运行客户端类的main方法测试
可以发现,原型对象和拷贝的对象中的
translate属性引用的已经不是同一个内存地址了。现在,基于深拷贝的原型模式也完成了。
总结
假如需要创建一个对象,但是创建对象的过程太过复杂,或者创建对象的成本太高,就可以考虑使用原型模式创建对象;原型模式避免了创建对象时重复的初始化工作,省去了创建对象时内部复杂逻辑的执行过程。原型模式提高了对象的创建效率。