您现在的位置是:亿华云 > 人工智能
五分钟 掌握 原型模式
亿华云2025-10-04 00:15:25【人工智能】6人已围观
简介大家好,我是老田,今天我给大家分享设计模式中的原型模式。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。故事还记得大四那年找工作,无意中我得从网上找到一份相对漂亮的程序
大家好,分钟我是掌握老田,今天我给大家分享设计模式中的原型原型模式。用贴切的模式生活故事,以及真实项目场景来讲设计模式,分钟最后用一句话来总结这个设计模式。掌握
故事
还记得大四那年找工作,原型无意中我得从网上找到一份相对漂亮的模式程序员简历模板,然后全班同学开启疯狂的分钟简历拷贝(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接口,实现快速复制。
创建对象的云南idc服务商四种方式:
new、反射、克隆、序列化
实际案例
大家是否有遇到过这种常见,就是项目中规定,不能把与数据库表映射的entity类返回给前端,所以通常返回给前端的有各种O,比如:XxxVO、XxxBO、XxxDTO...
这时候就会出现下面的场景,大家也想已经猜到了。
下面是与数据库表映射的UserEntity实体类。
public class UserEntity { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }返回给前端或者调用方的UserVO实体类。
public class UserVO { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }此时,从数据库里查出来的UserEntity需要转换成UserVO,然后再返回给前端(或者调用方)。
public class ObjectConvertUtil { public static UserVo convertUserEntityToUserVO(UserEntity userEntity) { if (userEntity == null) { return null; } UserVo userVo = new UserVo(); userVo.setId(userEntity.getId()); userVo.setName(userEntity.getName()); userVo.setAge(userEntity.getAge()); //如果还有更多属性呢? return userVo; } }从这个util类中,我们可以看出,如果一个类的属性有几十个,上百个的,这代码量是不是站群服务器有点恐怖?
于是,我们通常都会使用一些工具类来处理,比如常见有以下:
BeanUtils.copy(); JSON.parseObject() Guava工具类 .....这些工具类就用到了原型模式。
通过一个对象,创建一个新的对象。
也把原型模式称之为对象的拷贝、克隆。
其实对象的克隆分浅克隆和深克隆,下面我们就来聊聊浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。我们先来聊聊浅克隆,都喜欢由浅入深。
浅克隆
比如,我现在相对用户信息User进行克隆,但是User中有用户地址信息UserAddress属性。
以下是代码的实现:
//用户地址信息 public class UserAddress implements Serializable{ private String province; private String cityCode; public UserAddress(String province, String cityCode) { this.province = province; this.cityCode = cityCode; } } //用户信息 public class User implements Cloneable { private int age; private String name; //用户地址信息 private UserAddress userAddress; //getter setter 省略 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } //测试 public class UserTest { public static void main(String[] args) throws Exception { User user = new User(); user.setAge(20); user.setName("田维常"); UserAddress userAddress = new UserAddress("贵州", "梵净山"); user.setUserAddress(userAddress); User clone = (User) user.clone(); System.out.println("克隆前后UserAddress比较:" + (user.getUserAddress() == clone.getUserAddress())); } }输出结果
克隆前后 UserAddress 比较:true两个对象属性 UserAddress 指向的是同一个地址。
这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。
关系如下:
深克隆
关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成n多个孙悟空,手里都会拿着一个金箍棒。
按照前面的浅克隆,结果就是:孙悟空倒是变成很多孙悟空,但是金箍棒用的是同一根。
深克隆的结果是:孙悟空变成了很多个,金箍棒也变成很多个根。
下面我们用代码来实现:
//猴子,有身高体重和生日 public class Monkey { public int height; public int weight; public Date birthday; }孙悟空也是猴子,兵器 孙悟空有个金箍棒:
import java.io.Serializable; //孙悟空的金箍棒 public class JinGuBang implements Serializable{ public float h=100; public float d=10; //金箍棒变大 public void big(){ this.h *=10; this.d *=10; } //金箍棒变小 public void small(){ this.h /=10; this.d /=10; } }齐天大圣孙悟空:
import java.io.*; import java.util.Date; //孙悟空有七十二变,拔猴毛生成一个金箍棒 //使用JDK的克隆机制, //实现Cloneable并重写clone方法 public class QiTianDaSheng extends Monkey implements Cloneable, Serializable { public JinGuBang jinGuBang; public QiTianDaSheng() { this.birthday = new Date(); this.jinGuBang = new JinGuBang(); } @Override protected Object clone() throws CloneNotSupportedException { return this.deepClone(); } //深克隆 public QiTianDaSheng deepClone() { try { //内存中操作完成、对象读写,是通过字节码直接操作 //与序列化操作类似 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream bis = new ObjectInputStream(bais); //完成一个新的对象,底层是使用new创建的一个对象 //详情可以了解readObject方法 QiTianDaSheng qiTianDaSheng = (QiTianDaSheng) bis.readObject(); //每个猴子的生日不一样,所以每次拷贝的时候,把生日改一下 qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } catch (Exception ex) { ex.printStackTrace(); return null; } } //浅克隆,就是简单的赋值 public QiTianDaSheng shalllowClone(QiTianDaSheng target) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); qiTianDaSheng.height = target.height; qiTianDaSheng.weight = target.weight; qiTianDaSheng.jinGuBang = target.jinGuBang; qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } }接着我们就来测试一下:
public class DeepCloneTest { public static void main(String[] args) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); try { QiTianDaSheng newObject = (QiTianDaSheng) qiTianDaSheng.clone(); System.out.print("深克隆后 "); System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang)); } catch (Exception ex) { ex.printStackTrace(); } QiTianDaSheng newObject=qiTianDaSheng.shalllowClone(qiTianDaSheng); System.out.print("浅克隆后 "); System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang)); } }输出结果为:
深克隆后 金箍棒是否一直:false 浅克隆后 金箍棒是否一直:true结论
深克隆后每个孙悟空都有自己的金箍棒,而浅克隆后每个孙悟空用的金箍棒实质上还是同一根。
总结
切记:深和浅,指的是克隆对象里的属性(引用类型)是否指向同一个内存地址。
为了更深刻的理解深克隆和浅克隆,我们回答文中的简历拷贝的故事。
深拷贝:拷贝一份简历,然后对简历中的信息进行修改成自己的 浅拷贝:拷贝一份简历,简历内容完全不变优点:
Java 原型模式基于内存二进制流复制,比直接 new 的性能会更好一些。 可以利用深克隆保存对象状态,存一份旧的(克隆出来),在对其修改,可以充当一个撤销功能。缺点:
需要配置 clone 方法,改造时需要对已有类进行修改,违背 “开闭原则”。 如果对象间存在多重嵌套引用时,每一层都需要实现克隆。我们从原型模式的定义,使用场景,真实案例、浅克隆、深克隆、优缺点等方面,对原型模式进行了一个全面的讲解。
一句话总结:
一份简历,全班同学用
本文转载自微信公众号「Java后端技术全栈」,可以通过以下二维码关注。转载本文请联系Java后端技术全栈公众号。
很赞哦!(4)
相关文章
- 互联网其实拼的也是人脉,域名投资也是一个时效性很强的东西,一个不起眼的消息就会引起整个域名投资市场的动荡,因此拓宽自己的人脉圈,完善自己的信息获取渠道,让自己能够掌握更为多样化的信息,这样才更有助于自己的域名投资。
- 直击Titan图数据库:如何提升25%+的反欺诈检测效率?
- 数据库新动向 Oracle与微软割据局面产生
- RocketMQ消息丢失如何排查?
- 4.域名的整体品牌营销力
- 面试突击:如何使用线程池执行定时任务?
- 使用Python连接MySQL数据库
- 是否存在足够“通用”的处理器,在性能快速提升的同时,还能够“包治百病”?
- (4) 使用何种形式的域名后缀对网页搜索影响不大,但域名后缀也需要考虑方便用户记忆
- 3分钟深入学习Redis的高可用特性“持久化”