大家好,我是老田,今天给大家分享的是设计模式中的原型模式。用恰当的生活故事和真实的项目场景来描述设计模式,最后用一句话概括这个设计模式。故事还记得大四找工作的时候,无意中从网上找到了一份比较漂亮的程序员简历模板,然后全班同学开始疯狂抄简历(U盘)。同时,还闹出了笑话。有的同学抄过去的简历,内容和名字一点都没改,投给面试官(校招面试官)。后面大家应该也能猜到结果了。大家都去实习了,还有人还在找工作。后来公司面试官和同学的反馈:收到了一份一毛钱的简历,复印了好几份,回来大家就知道问题出在哪里了。我承认我抄过去没改,拿出来投稿。令人尴尬和尴尬。其中一匹马。简历复制有两种:一种是复制简历,然后修改信息为自己的;另一种是复制简历而不改变内容。原型模式定义使用原型实例指定创建对象的种类,并通过复制此原型来创建新对象。原型模式:PrototypePattern,属于创造模式。调用者不需要知道任何创建细节,也不需要调用构造函数来创建对象。使用场景原型模式有以下几种使用场景:类初始化消耗大量资源new生成的对象需要非常繁琐的过程(数据准备、访问权限等)构造函数相对复杂当对象数量较多时在循环体中生成在Spring中,原型模式的应用非常广泛,例如:scope='prototype'我们可以将一些getter和setter封装成一个工厂方法,然后对于用户来说,调用方法而不知道如何处理getter和setter。我们也可以使用JDK提供的Cloneable接口来实现快速复制。创建对象的四种方法:新建、反射、克隆和序列化。你遇到过这种常见的情况吗?项目中规定不能将与数据库表映射的实体类返回给前端,所以一般都是返回给前端。有各种各样的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("克隆比较ofUserAddressbeforeandafter:"+(user.getUserAddress()==clone.getUserAddress()));}}输出结果UserAddress克隆前后比较:true两个对象属性UserAddress指向同一个地址。这就是所谓的浅克隆,就是把对象克隆出来。对于对象的非基本类型属性,它仍然指向原对象的属性所指向的对象的内存地址。关系如下:深度克隆关于深度克隆,我们用一个非常经典的案例,西游记中的孙悟空。一个孙悟空可以变成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方法);}//深度克隆publicQiTianDaShengdeepClone(){try{//内存中的操作完成,对对象进行读写操作,直接用字节码操作//类似序列化操作(this);ByteArrayInputStreambais=newByteArrayInputStream(bos.toByteArray());ObjectInputstreambis=newObjectInputStream(bais);//完成一个new对象,底层是使用new创建的对象//具体可以了解readObject方法QiTianDaShengqiTianDaSheng=(QiTianDaSheng)bis.readObject();//每只猴子的生日不一样,所以每次复制的时候把生日改成qiTianDaSheng.birthday=newDate();returnqiTianDaSheng;}catch(Exceptionex){ex.printStackTrace();returnnull;}}//浅克隆很简单assignmentpublicQiTianDaShengshalllowClone(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("Afterdeepcloning");System.out.println("无论去lden棍棒总是:“+(qiTianDaSheng.jinGuBang==newObject.jinGuBang));}catch(Exceptionex){ex.printStackTrace();}QiTianDaShengnewObject=qiTianDaSheng.shalllowClone(qiTian大胜);System.out.print("浅克隆后");System.out.println("金箍棒是否始终:"+(qiTianDaSheng.jinGuBang==newObject.jinGuBang));}}输出结果为:deep克隆后金箍棒是否一致:falseconstantaftershallowcloned:true结论深克隆后,每个孙悟空都有自己的金箍棒,但是浅克隆后,每个孙悟空使用的金箍棒基本相同根总结记住:深浅,指的是属性是否克隆对象中的(引用类型)指向相同的内存地址。为了对深克隆和浅克隆有更深入的理解,我们在文中对简历文案的故事进行解答。深拷贝:复制一份简历,然后将简历中的信息修改为自己的浅拷贝:复制一份简历,简历内容不变。优点:Java原型模式是基于内存二进制流拷贝,性能比directnew好一点。您可以使用深度克隆来保存对象的状态,保存一个旧的(克隆的)并对其进行修改,这可以作为撤消功能。缺点:clone方法需要配置,改造时需要修改已有的类,违反了“开闭原则”。如果对象之间存在多个嵌套引用,则每一层都需要实现克隆。我们从原型模式的定义、使用场景、真实案例、浅克隆、深度克隆、优缺点等方面对原型模式进行了全面的讲解。一句话总结:一份简历,全班同学用这篇文章转载来自微信公众号“Java后端技术全栈”,可通过以下二维码关注。转载本文请联系Java后端技术全栈公众号。
