Java设计模式——创建型模式——原型模式(Prototype Pattern)
原型模式概念
原型模式(Prototype Pattern):与工厂模式类似,都是用来创建对象的。利用克隆来生成一个大对象,减少创建时的初始化等操作占用开销
原型模式属于对象的创建模式,通过给出一个原型对象来指明要创建对象的类型,然后用复制这个原型对象的办法来创建出更多的同类型的对象。就是给你一个现有的对象,你要得到和这个对象同类型的新对象,就将这个对象复制一下,就可以得到了。
该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。java中复制通过clone()实现的。clone中涉及深、浅复制。深、浅复制的概念如下:
⑴浅复制(浅克隆)
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址
⑵深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
Java对象的复制:
Java的所有类都是从java.lang.Object类继承而来的,而object类提供下面的方法对对象进行复制:protected Object clone()
子类可以把这个方法置换掉,每个类的情况不同,置换后提供一个满足自己的clone方法。值得注意的是,对象复制有一个问题需要注意,通常一个对象内部有对其它对象的引用,就是其它对象是该对象的一个组成部分,当使用object类的clone方法复制对象的时候,此对象的其它对象引用同时也会被复制一份。
Java提供的cloneable接口起了一个作用,就是告诉java虚拟机可以安全的在该类上使用clone方法。不过Object本身并没有实现cloneable接口,因此如果要复制的类没有实现cloneable接口,调用clone方法会抛出CloneNotSupportedException异常。
为什么需要原型模式
1,有些时候,我们需要创建多个类似的大对象。如果直接通过new对象,开销很大,而且new完还得进行重复的初始化工作。可能把初始化工作封装起来的,但是对于系统来说,你封不封装,初始化工作还是要执行。,
2,原型模式则不同,原型模式是先创建好一个原型对象,然后通过clone这个原型对象来创建新的对象,这样就免去了重复的初始化工作,系统仅需内存拷贝即可。
class Prototype implements Cloneable {//实现cloneable接口 public Prototype clone(){ //根据自己的要求,置换了clone方法 Prototype prototype = null; try{ prototype= (Prototype)super.clone(); }catch(CloneNotSupportedException e){ e.printStackTrace(); } return prototype; } }
/** * Created with IntelliJ IDEA. * User:by gyw * Date:2018-03-31 20:42 */ 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); } }
/** * Created with IntelliJ IDEA. * User:by gyw * Date:2018-03-31 20:43 */ 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()); } }----------------A--------------
姓名:小李子
生日:2.16,性别:男,毕业学校:XX大学
工作年限:2012.09.05,公司:XX科技有限公司
----------------B--------------
姓名:小李子
生日:2.16,性别:男,毕业学校:XX大学
工作年限:2012.09.05,公司:XX科技有限公司
A==B?false
A.getClass()==B.getClass()?true
使用场景
1、如果创建新对象成本较大,我们可以利用已有的对象进行复制来获得。
2、如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。
3、需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
模式总结
1、原型模式向客户隐藏了创建对象的复杂性。客户只需要知道要创建对象的类型,然后通过请求就可以获得和该对象一模一样的新对象,无须知道具体的创建过程。
2、克隆分为浅克隆和深克隆两种。
3、我们虽然可以利用原型模式来获得一个新对象,但有时对象的复制可能会相当的复杂,比如深克隆。