设计模式:学习笔记(7)——原型模式
设计模式:学习笔记(7)——原型模式
快速开始
介绍
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
克隆我们都清楚,就是用一个物体复制若干个一模一样物体。同样,在面向对象系统中,我们同样可以利用克隆技术来克隆出若干个一模一样的对象。在应用程序中,有些对象比较复杂,其创建过程过于复杂,而且我们又需要频繁的利用该对象,如果这个时候我们按照常规思维new该对象,那么务必会带来非常多的麻烦,这个时候我们就希望可以利用一个已有的对象来不断对他进行复制就好了,这就是编程中的“克隆”。这里原型模式就可以满足我们的“克隆”,在原型模式中我们可以利用过一个原型对象来指明我们所要创建对象的类型,然后通过复制这个对象的方法来获得与该对象一模一样的对象实例。这就是原型模式的设计目的。
看到这里思考到这里,我才对原型模式有了一定的理解:我们可能费尽千山万水的功夫创建了一个复杂对象,而现在我们又要其他几个和这个几乎一样的对象,我们就不用在new,我们直接用Java提供的深度复制来实现对象的拷贝。
模式原理
我们都知道Object是祖宗,所有的Java类都继承至Object,而Object类提供了一个clone()方法,该方法可以将一个java对象复制一份,因此在java中可以直接使用clone()方法来复制一个对象。但是需要实现clone的Java类必须要实现一个接口:Cloneable.该接口表示该类能够复制且具体复制的能力,如果不实现该接口而直接调用clone()方法会抛出CloneNotSupportedException异常。如下:
public class PrototypeDemo implements Cloneable{ public Object clone(){ Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("Not support cloneable"); } return object; } …… }
Java中任何实现了Cloneable接口的类都可以通过调用clone()方法来复制一份自身然后传给调用者。一般而言,clone()方法满足:
(1) 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象。
(2) 对任何的对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原对象的类型一样。
(3) 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
实例分析
复印简历各位都应该做过吧!这里我们利用原型模式来模拟复印简历。
简历:Resume.java
public class Resume implements Cloneable { private String name; private String birthday; private String sex; private String school; private String timeArea; private String company; /** * 构造函数:初始化简历赋值姓名 */ public Resume(String name){ this.name = name; } /** * @desc 设定个人基本信息 * @param birthday 生日 * @param sex 性别 * @param school 毕业学校 * @return void */ public void setPersonInfo(String birthday,String sex,String school){ this.birthday = birthday; this.sex = sex; this.school = school; } /** * @desc 设定工作经历 * @param timeArea 工作年限 * @param company 所在公司 * @return void */ public void setWorkExperience(String timeArea,String company){ this.timeArea = timeArea; this.company = company; } /** * 克隆该实例 */ public Object clone(){ Resume resume = null; try { resume = (Resume) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return resume; } public void display(){ System.out.println("姓名:" + name); System.out.println("生日:" + birthday + ",性别:" + sex + ",毕业学校:" + school); System.out.println("工作年限:" + timeArea + ",公司:" + company); } }
客户端:Client.java
public class Client { public static void main(String[] args) { //原型A对象 Resume a = new Resume("小李子"); a.setPersonInfo("2.16", "男", "XX大学"); a.setWorkExperience("2012.09.05", "XX科技有限公司"); //克隆B对象 Resume b = (Resume) a.clone(); //输出A和B对象 System.out.println("----------------A--------------"); a.display(); System.out.println("----------------B--------------"); b.display(); /* * 测试A==B? * 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象 */ System.out.print("A==B?"); System.out.println( a == b); /* * 对任何的对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原对象的类型一样。 */ System.out.print("A.getClass()==B.getClass()?"); System.out.println(a.getClass() == b.getClass()); } }
为什么用Super.clone()
1.Object中的clone执行的时候使用了RTTI(run-time type identification)的机制,动态得找到目前正在调用clone方法的那个reference,根据它的大小申请内存空间,然后进行bitwise的复制,将该对象的内存空间完全复制到新的空间中去,从而达到shallowcopy的目的。 所以你调用super.clone() 得到的是当前调用类的副本,而不是父类的副本。根本没有必用调用this.clone();
2.要让实例调用clone方法就需要让此类实现Cloneable接口,API里面还有句话是:如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常,这便是“合法”的含义。 但请注意,Cloneable接口只是个标签接口,不含任何需要实现的方法,就像Serializable接口一样。
总之,一般如果你的子类没有特殊需要而重写clone()方法就直接用super.clone() 就行了。