文章目录

  • 引例
  • 原型模式
  • 浅拷贝
  • 深拷贝

引例


标题无意冒犯
在介绍原型模式前,我们先从实际问题出发,对比解决方法前后优劣点。

问题
现在有一只羊(包含属性:名字Dolly、年龄2),需要克隆10只属性完全相同的羊。

一般解法

  1. 定义Sheep类表示羊,包括构造器、getter()和toString()。
public class Sheep {private String name;private int age;public Sheep(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "Sheep{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  1. 在客户端实例化多利,然后再根据多利的属性去实例化10只羊。
public class Client {public static void main(String[] args) {Sheep sheepDolly=new Sheep("Dolly",2);Sheep sheep1 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());Sheep sheep2 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());Sheep sheep3 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());//....System.out.println(sheep1+",hashCode:"+sheep1.hashCode());System.out.println(sheep2+",hashCode:"+sheep2.hashCode());System.out.println(sheep3+",hashCode:"+sheep3.hashCode());//...}
}
  1. 运行结果

优缺点
这种方法是我们首先很容易就能想到的,也是绝大多数人的第一做法。
但缺点也很明显,每次创建新对象时需要获取原始对象的属性,对象复杂时效率很低;此外不能动态获得对象运行时的状态,若类增减属性需要改动代码。

下面我们看下原型模式的解法。

原型模式


原型模式(Prototype Pattern)是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。即用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。

工作原理:将原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。即用基类Object的clone()方法或序列化。

UML类图

  • Prototype:原型类,声明一个克隆自己的接口
  • ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
  • Client: 客户端让一个原型对象克隆自己,从而创建一个新的对象

原型模式又可分为浅拷贝和深拷贝,区别在于对引用数据类型的成员变量的拷贝,小朋友你是否有很多问号? 不急 ,看完这两种方法实现你就懂了。

浅拷贝


  1. 在原先Sheep类基础上实现Cloneable接口,重写clone方法。
public class Sheep implements Cloneable{private String name;private int age;@Overrideprotected Object clone()  {//克隆该实例,使用默认的clone方法来完成Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return sheep;}public Sheep(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Sheep{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  1. 客户端调用
public class Client {public static void main(String[] args) {Sheep sheepDolly=new Sheep("Dolly",2);Sheep sheep1 = (Sheep)sheepDolly.clone();Sheep sheep2 = (Sheep)sheepDolly.clone();Sheep sheep3 = (Sheep)sheepDolly.clone();//....System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());//...}
}
  1. 运行结果

    至此,原型模式的浅拷贝也成功克隆了三个对象,但是看进度条发现并不简单。

现在小羊有了一个朋友小牛,Sheep类添加了一个引用属性Cow,我们同样再克隆一遍。

  1. Sheep类
public class Sheep implements Cloneable{private String name;private int age;public Cow friend;//新朋友Cow对象,其余不变@Overrideprotected Object clone()  {Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return sheep;}public Sheep(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Sheep{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  1. 新添的Cow类
public class Cow {private String name;private int age;public Cow(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Cow{" +"name='" + name + '\'' +", age=" + age +'}';}
}

(插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/

  1. 客户端调用克隆
public class Client {public static void main(String[] args) {Sheep sheepDolly=new Sheep("Dolly",2);sheepDolly.friend=new Cow("Tom",1); //并实例化朋友Sheep sheep1 = (Sheep)sheepDolly.clone();Sheep sheep2 = (Sheep)sheepDolly.clone();Sheep sheep3 = (Sheep)sheepDolly.clone();//....System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');//...}
}
  1. 运行结果

通过运行结果发现,浅拷贝通过Object的clone()成功克隆实例化了三个新对象,但是并没有克隆实例化对象中的引用属性,也就是没有克隆friend对象(禁止套娃 ),三个新克隆对象的friend还是指向原克隆前的friend,即同一个对象。

这样的话,他们四个的friend是引用同一个,若一个对象修改了friend属性,势必会影响其他三个对象的该成员变量值。

小结

  • 浅拷贝是使用默认的 clone()方法来实现
  • 基本数据类型的成员变量,浅拷贝会直接进行值传递(复制属性值给新对象)。
  • 引用数据类型的成员变量,浅拷贝会进行引用传递(复制引用值(内存地址)给新对象)。

深拷贝


方法一:
机灵的人儿看出,再clone一遍cow不就好了,但是手动递归下去不推荐。

  1. Cow类也实现Cloneable接口
public class Cow implements Cloneable{private String name;private int age;public Cow(String name, int age) {this.name = name;this.age = age;}//无引用类型,直接clone即可@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone(); //直接抛出了,没用try-catch}@Overridepublic String toString() {return "Cow{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  1. Sheep类的clone再添加调用cow的clone
public class Sheep implements Cloneable{private String name;private int age;public Cow friend;//新朋友Cow对象,其余不变@Overrideprotected Object clone() throws CloneNotSupportedException {Object deep = null;//完成对基本数据类型(属性)和String的克隆deep = super.clone();//对引用类型的属性,进行再次cloneSheep sheep = (Sheep)deep;sheep.friend  = (Cow)friend.clone();return sheep;}public Sheep(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Sheep{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  1. 客户端调用
public class Client {public static void main(String[] args) throws CloneNotSupportedException {Sheep sheepDolly=new Sheep("Dolly",2);sheepDolly.friend=new Cow("Tom",1); //并实例化朋友Sheep sheep1 = (Sheep)sheepDolly.clone();Sheep sheep2 = (Sheep)sheepDolly.clone();Sheep sheep3 = (Sheep)sheepDolly.clone();//....System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');//...}
}
  1. 运行结果

方法二:
通过对象序列化实现深拷贝(推荐)

  1. Cow类实现序列化接口,不必实现Cloneable接口了
public class Cow implements Serializable {private String name;private int age;public Cow(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Cow{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  1. 在Sheep类实现序列化接口
public class Sheep implements Serializable { //实现序列化接口private String name;private int age;public Cow friend;public Sheep(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Sheep{" +"name='" + name + '\'' +", age=" + age +'}';}public Object deepClone() { //深拷贝//创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); //当前这个对象以对象流的方式输出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);Sheep sheep = (Sheep) ois.readObject();return sheep;} catch (Exception e) {e.printStackTrace();return null;} finally {//关闭流try {bos.close();oos.close();bis.close();ois.close();} catch (Exception e2) {System.out.println(e2.getMessage());}}}
}
  1. 客户端调用
public class Client {public static void main(String[] args) throws CloneNotSupportedException {Sheep sheepDolly=new Sheep("Dolly",2);sheepDolly.friend=new Cow("Tom",1); //并实例化朋友Sheep sheep1 = (Sheep)sheepDolly.deepClone();Sheep sheep2 = (Sheep)sheepDolly.deepClone();Sheep sheep3 = (Sheep)sheepDolly.deepClone();//....System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');//...}
}
  1. 运行结果

原型模式总结

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提
    高效率
  • 可以不用重新初始化对象,动态地获得对象运行时的状态。
  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  • 若成员变量无引用类型,浅拷贝clone即可;若引用类型的成员变量很少,可考虑递归实现clone,否则推荐序列化。

原创不易,请勿转载(本不富裕的访问量雪上加霜 )
博主首页:https://wzlodq.blog.csdn.net/
微信公众号:吾仄lo咚锵
如果文章对你有帮助,记得一键三连❤

设计模式-原型模式(克隆羊多利看了都说好)相关推荐

  1. C++设计模式——原型模式(Prototype Pattern)

    C++设计模式--原型模式(Prototype Pattern) 微信公众号:幼儿园的学霸 目录 文章目录 C++设计模式--原型模式(Prototype Pattern) 目录 定义 代码示例 普通 ...

  2. 设计模式 原型模式_创新设计模式:原型模式

    设计模式 原型模式 原型模式用于创建对象的副本. 这种模式非常有用,特别是当从头开始创建对象的成本很高时. 与builder , factory和abstract factory模式相比,它不会从头开 ...

  3. 设计模式 原型模式_设计模式:原型

    设计模式 原型模式 创新设计模式之一是原型设计模式 . 尽管原型是创造模式,但它在概念上与其他模式有所区别. 我的意思是原型在某种意义上创造了自己. 我将在下面解释. 原型模式的所有魔力都基于Java ...

  4. Java常用设计模式————原型模式(一)

    介绍 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 原型模式用于创建重复的对象,同时又能保证性能.当直接创建对象的代价比较大时,则采用 ...

  5. 设计模式 原型模式_原型设计模式:创建另一个小车

    设计模式 原型模式 创建对象确实是一个耗时的过程,也是一件昂贵的事情. 因此,我们现在正冒险节省时间和金钱. 我们该怎么做? 克隆奇迹多莉 有人记得多莉吗? 是的,是绵羊,是第一个被克隆的哺乳动物. ...

  6. 第六章 Caché 设计模式 原型模式

    文章目录 第六章 Caché 设计模式 原型模式 定义 使用场景 优点 结构图 描述 示例 初级写法 缺点 中级写法 缺点 高级写法 (浅复制) 浅复制 深复制 完整示例 简历类(复制类) 对象类(工 ...

  7. 我的Java设计模式-原型模式

    "不好意思,我是卧底!哇哈哈哈~"额......自从写了上一篇的观察者模式,就一直沉浸在这个角色当中,无法自拨.昨晚在看<使徒行者2>,有一集说到啊炮仗哥印钞票,我去, ...

  8. 『设计模式』设计模式--原型模式

    23种设计模式+额外常用设计模式汇总 (持续更新) 1.概念: 原型模式(Prototype Pattern)是通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同 ...

  9. 设计模式-原型模式与模板方法模式的对比总结

    前言: 前面几个设计模式几乎都是以代码的形式进行汇总,和解析具体的流程.这节原型模式和模板方法模式大致上我觉得是有很多相似的地方,所以就来总结下,那些地方相同.那些地方不同! 对比: 相同点: 1,都 ...

最新文章

  1. java 加密算法 base64
  2. python 表格格式输出_利用python对excel中一列的时间数据更改格式操作
  3. 阿里公开Weex技术架构,还开源了一大波组件
  4. 小米10pro使用说明书_华为Mate40、华为P40和小米10拍照对比:哪一款最好?
  5. spring cloud服务发现注解之@EnableDiscoveryClient与@EnableEurekaClient 1
  6. 昨天考了网工,一点感想
  7. 如何一个月减重20斤?
  8. 解决 Qt5 报错 This application failed to start because it could not find or load the Qt platform plugin
  9. Unable to start debugging The Silverlight Developer Runtime is not installed
  10. ToolTip特效 JavaScript 盗取厦门人才网的特效
  11. dtcms传递多个参数怎么获取
  12. 烟台移动dns网关_如何设置移动宽带DNS和IP地址
  13. webservice 缺少根元素_草莓种植,这2种元素至关重要,直接影响草莓的产量和品质...
  14. Linux的关键字搜索
  15. 使用userAgent区分浏览器版本
  16. springboot-grpc
  17. 检测网络不通时自动重启计算机,电脑连不上网怎么办?6个最简单最实用的解决办法,帮你轻松搞定!...
  18. Salesforce系列(六):Salesforce Apex基础SOQL查询和数据添加!
  19. vscode代码格式化、vetur代码格式化取消末尾添加逗号
  20. ChatGPT中文网 - ChatGPT国内网页版在线使用

热门文章

  1. BUU[GWCTF 2019]pyre 1
  2. Unable to save binary node-sass/vendor/linux-x64-72 : Error: EACCES: permission denied, mkdir
  3. c语言 crc16 不是8的整数倍,当输入是奇数位(不是字节)时生成CRC8/16的最佳方法?C或Python...
  4. 五轴数控工具磨床控制系统解决方案
  5. 八小时之内、八小时之外
  6. 基于Docker搭建MySQL(MariaDB)+ mycat读写分离测试环境
  7. 我的2017云栖之行
  8. 移动商城第五篇(用户模块)【用户登陆、回显用户、拦截器、收货地址】
  9. 一文搞懂cookie、session、token、jwt、OAuth
  10. 爬虫之js逆向(易车网实例)