Java中原型模式的示例分析

这篇文章将为大家详细讲解有关Java中原型模式的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

故事

还记得大四那年找工作,无意中我得从网上找到一份相对漂亮的程序员简历模板,然后全班同学开启疯狂的简历拷贝(U盘)。同时也闹出了一个笑话,有几位同学,拷贝过去的简历,内容完全没改,名字都没有改,截止投给面试官(校招面试官)。后来,结果大家也应该能猜出来,大家都去实习了,部分人还在找工作。后面公司面试官和同伴的其他同学反馈:收到一毛一样的简历,好几份,回来大家一聊就知道问题出哪里了,承认了自己拷贝过去完全没改就拿出去投了,害,尴尬的一匹。

把简历拷贝分为为两种:

  • 一种是拷贝简历,然后把信息修改成自己的

  • 另外一种是,拷贝简历,内容什么都不改。

原型模式定义

Specify the kinds of objects to create using a prototype instance ,and create new objects by coping this prototype

大致意思:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

原型模式:Prototype Pattern,属于创建型模式。

调用者不需要知道任何创建细节,也不用调用构造方法来创建对象。

使用场景

原型模式有如下使用场景:

  • 类初始化消耗资源较多

  • new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)

  • 构造函数比较复杂

  • 循环体内生成大量对象时

  • 在Spring中,原型模式应用的非常广泛,例如:scope='prototype'

我们可以将一些getter和setter之类封装成一个工厂方法,然后对于使用的人来说,调用方法就可以了,不需要知道里面的getter和setter是怎么处理的。我们也可以使用JDK提供的实现Cloneable接口,实现快速复制。

创建对象的四种方式:

new、反射、克隆、序列化

实际案例

大家是否有遇到过这种常见,就是项目中规定,不能把与数据库表映射的entity类返回给前端,所以通常返回给前端的有各种O,比如:XxxVO、XxxBO、XxxDTO...

这时候就会出现下面的场景,大家也想已经猜到了。

下面是与数据库表映射的UserEntity实体类。

publicclassUserEntity{privateLongid;privateStringname;privateIntegerage;//....可能还有很多属性//省略gettersetter}

返回给前端或者调用方的UserVO实体类。

publicclassUserVO{privateLongid;privateStringname;privateIntegerage;//....可能还有很多属性//省略gettersetter}

此时,从数据库里查出来的UserEntity需要转换成UserVO,然后再返回给前端(或者调用方)。

publicclassObjectConvertUtil{publicstaticUserVoconvertUserEntityToUserVO(UserEntityuserEntity){if(userEntity==null){returnnull;}UserVouserVo=newUserVo();userVo.setId(userEntity.getId());userVo.setName(userEntity.getName());userVo.setAge(userEntity.getAge());//如果还有更多属性呢?returnuserVo;}}

从这个util类中,我们可以看出,如果一个类的属性有几十个,上百个的,这代码量是不是有点恐怖?

于是,我们通常都会使用一些工具类来处理,比如常见有以下:

BeanUtils.copy();JSON.parseObject()Guava工具类.....

这些工具类就用到了原型模式。

通过一个对象,创建一个新的对象。

也把原型模式称之为对象的拷贝、克隆。

其实对象的克隆分浅克隆和深克隆,下面我们就来聊聊浅克隆和深克隆。

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。

  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

我们先来聊聊浅克隆,都喜欢由浅入深。

浅克隆

比如,我现在相对用户信息User进行克隆,但是User中有用户地址信息UserAddress属性。

以下是代码的实现:

//用户地址信息publicclassUserAddressimplementsSerializable{privateStringprovince;privateStringcityCode;publicUserAddress(Stringprovince,StringcityCode){this.province=province;this.cityCode=cityCode;}}//用户信息publicclassUserimplementsCloneable{privateintage;privateStringname;//用户地址信息privateUserAddressuserAddress;//gettersetter省略@OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnsuper.clone();}}//测试publicclassUserTest{publicstaticvoidmain(String[]args)throwsException{Useruser=newUser();user.setAge(20);user.setName("田维常");UserAddressuserAddress=newUserAddress("贵州","梵净山");user.setUserAddress(userAddress);Userclone=(User)user.clone();System.out.println("克隆前后UserAddress比较:"+(user.getUserAddress()==clone.getUserAddress()));}}

输出结果

克隆前后UserAddress比较:true

两个对象属性 UserAddress 指向的是同一个地址。

这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。

关系如下:

Java中原型模式的示例分析

深克隆

关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成n多个孙悟空,手里都会拿着一个金箍棒。

按照前面的浅克隆,结果就是:孙悟空倒是变成很多孙悟空,但是金箍棒用的是同一根。

深克隆的结果是:孙悟空变成了很多个,金箍棒也变成很多个根。

下面我们用代码来实现:

//猴子,有身高体重和生日publicclassMonkey{publicintheight;publicintweight;publicDatebirthday;}

孙悟空也是猴子,兵器 孙悟空有个金箍棒:

importjava.io.Serializable;//孙悟空的金箍棒publicclassJinGuBangimplementsSerializable{publicfloath=100;publicfloatd=10;//金箍棒变大publicvoidbig(){this.h*=10;this.d*=10;}//金箍棒变小publicvoidsmall(){this.h/=10;this.d/=10;}}

齐天大圣孙悟空:

importjava.io.*;importjava.util.Date;//孙悟空有七十二变,拔猴毛生成一个金箍棒//使用JDK的克隆机制,//实现Cloneable并重写clone方法publicclassQiTianDaShengextendsMonkeyimplementsCloneable,Serializable{publicJinGuBangjinGuBang;publicQiTianDaSheng(){this.birthday=newDate();this.jinGuBang=newJinGuBang();}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnthis.deepClone();}//深克隆publicQiTianDaShengdeepClone(){try{//内存中操作完成、对象读写,是通过字节码直接操作//与序列化操作类似ByteArrayOutputStreambos=newByteArrayOutputStream();ObjectOutputStreamoos=newObjectOutputStream(bos);oos.writeObject(this);ByteArrayInputStreambais=newByteArrayInputStream(bos.toByteArray());ObjectInputStreambis=newObjectInputStream(bais);//完成一个新的对象,底层是使用new创建的一个对象//详情可以了解readObject方法QiTianDaShengqiTianDaSheng=(QiTianDaSheng)bis.readObject();//每个猴子的生日不一样,所以每次拷贝的时候,把生日改一下qiTianDaSheng.birthday=newDate();returnqiTianDaSheng;}catch(Exceptionex){ex.printStackTrace();returnnull;}}//浅克隆,就是简单的赋值publicQiTianDaShengshalllowClone(QiTianDaShengtarget){QiTianDaShengqiTianDaSheng=newQiTianDaSheng();qiTianDaSheng.height=target.height;qiTianDaSheng.weight=target.weight;qiTianDaSheng.jinGuBang=target.jinGuBang;qiTianDaSheng.birthday=newDate();returnqiTianDaSheng;}}

接着我们就来测试一下:

publicclassDeepCloneTest{publicstaticvoidmain(String[]args){QiTianDaShengqiTianDaSheng=newQiTianDaSheng();try{QiTianDaShengnewObject=(QiTianDaSheng)qiTianDaSheng.clone();System.out.print("深克隆后");System.out.println("金箍棒是否一直:"+(qiTianDaSheng.jinGuBang==newObject.jinGuBang));}catch(Exceptionex){ex.printStackTrace();}QiTianDaShengnewObject=qiTianDaSheng.shalllowClone(qiTianDaSheng);System.out.print("浅克隆后");System.out.println("金箍棒是否一直:"+(qiTianDaSheng.jinGuBang==newObject.jinGuBang));}}

输出结果为:

深克隆后金箍棒是否一直:false浅克隆后金箍棒是否一直:true

结论

深克隆后每个孙悟空都有自己的金箍棒,而浅克隆后每个孙悟空用的金箍棒实质上还是同一根。

Java中原型模式的示例分析

总结

切记:深和浅,指的是克隆对象里的属性(引用类型)是否指向同一个内存地址。

为了更深刻的理解深克隆和浅克隆,我们回答文中的简历拷贝的故事。

  • 深拷贝:拷贝一份简历,然后对简历中的信息进行修改成自己的

  • 浅拷贝:拷贝一份简历,简历内容完全不变

优点:

  • Java 原型模式基于内存二进制流复制,比直接 new 的性能会更好一些。

  • 可以利用深克隆保存对象状态,存一份旧的(克隆出来),在对其修改,可以充当一个撤销功能。

缺点:

  • 需要配置 clone 方法,改造时需要对已有类进行修改,违背 “开闭原则”。

  • 如果对象间存在多重嵌套引用时,每一层都需要实现克隆。

我们从原型模式的定义,使用场景,真实案例、浅克隆、深克隆、优缺点等方面,对原型模式进行了一个全面的讲解。

关于“Java中原型模式的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

发布于 2021-06-13 23:18:52
收藏
分享
海报
0 条评论
186
上一篇:Java中怎么实现微信退款功能 下一篇:Android中微信小程序的图片优化技巧分享
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码