专栏首页程序员的成长之路23种设计模式之——原型模式

23种设计模式之——原型模式

什么是原型模式?

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式其实就是一个对象在创建另一个可定制的对象,而且不需要指定任何创建的细节。Java提供了Coneable接口,其中有一个唯一方法Clone(),实现这个接口就可以完成原型模式了。

什么时候使用它呢?

一般在初始化的信息不发生变化的情况下,克隆是最好的办法。既隐藏了对象创建的细节,又对性能是大大的提高。不用重新初始化对象,而是动态地获得对象运行时的状态。

面试的时候需要递交简历,很多家公司需要去面试,一份简历就需要复印很多份,下面通过简历的事件来模拟原型模式。

原型模式实现:

简历类

/** * 作者:LKP * 时间:2018/7/27 * 简历类 */public class Resume implements Cloneable {private String name;private String sex;private String age;private String timeArea;private String company;public Resume(String name){this.name = name;}//设置个人信息public void SetPersonalInfo(String sex,String age){this.sex = sex;this.age = age;}//设置工作经历public void SetWorkExperience(String timeArea,String company){this.timeArea = timeArea;this.company = company;}//显示public void Display(){System.out.println(name+"\t"+sex+"\t"+age);System.out.println("工作经历:"+timeArea+"\t"+company);}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}}

客户端实现:

/** * 作者:LKP * 时间:2018/7/27 * 客户端 */public class Client {public static void main(String[] args) throws CloneNotSupportedException {Resume a = new Resume("小明");a.SetPersonalInfo("男","29");a.SetWorkExperience("1998-2000","xx公司");Resume b = (Resume) a.clone();b.SetWorkExperience("1998-2018","YY公司");Resume c = (Resume) a.clone();c.SetPersonalInfo("男","24");a.Display();b.Display();c.Display();}}

运行结果

只需要调用Clone方法就可以实现新简历的生成,并且可以再修改新简历的细节。

它的好处,不用重新初始化对象,而是动态地获得对象运行时的状态。

原型模式有两种情况:

一种是浅复制,一种是深复制。

Clone()方法是这样的,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

接下来我们来看看这一现象:

新增工作经历类:

/** * 作者:LKP * 时间:2018/7/27 * 工作经历类 */public class WorkExperience {private String workDate;private String company;public String getWorkDate() {return workDate;}public void setWorkDate(String workDate) {this.workDate = workDate;}public String getCompany() {return company;}public void setCompany(String company) {this.company = company;}}

修改简历类:

引用“工作经历”对象;在“简历”类实例化时同时实例化“工作经历”。

/** * 作者:LKP * 时间:2018/7/27 * 简历类 */public class Resume implements Cloneable {private String name;private String sex;private String age;private WorkExperience work;public Resume(String name){this.name = name;work = new WorkExperience();}//设置个人信息public void SetPersonalInfo(String sex,String age){this.sex = sex;this.age = age;}//设置工作经历public void SetWorkExperience(String workDate,String company){work.setWorkDate(workDate);work.setCompany(company);}//显示public void Display(){System.out.println(name+"\t"+sex+"\t"+age);System.out.println("工作经历:"+work.getWorkDate()+"\t"+work.getCompany());}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}}

客户端代码进行细微修改:

/** * 作者:LKP * 时间:2018/7/27 * 客户端 */public class Client {public static void main(String[] args) throws CloneNotSupportedException {Resume a = new Resume("小明");a.SetPersonalInfo("男","29");a.SetWorkExperience("1998-2000","xx公司");Resume b = (Resume) a.clone();b.SetWorkExperience("1998-2018","YY公司");Resume c = (Resume) a.clone();a.SetWorkExperience("1998-2000","ZZ公司");a.Display();b.Display();c.Display();}}

b和c都克隆于a,但当它们都设置了“工作经历”时,我们希望的结果是三个的显示不一样。

运行结果:

可惜,没有达到我们的要求,三次显示的结果都是最后一次设置的值。

这种原因,就叫做”浅复制“,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

我们可能更需要这样一个需求:把要复制的对象所引用的对象都复制一遍。

比如刚刚的例子,我们希望是a、b、c三个引用的对象都是不同的,复制时就一变二,二变三。这种复制的方式叫做“深复制”,深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

深复制代码实现:

修改我们刚刚创建的工作经历类,让其实现Coneable。“工作经历”类实现克隆方法。

/** * 作者:LKP * 时间:2018/7/27 * 工作经历类 */public class WorkExperience implements Cloneable{private String workDate;private String company;public String getWorkDate() {return workDate;}public void setWorkDate(String workDate) {this.workDate = workDate;}public String getCompany() {return company;}public void setCompany(String company) {this.company = company;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}}

简历类修改如下:

/** * 作者:LKP * 时间:2018/7/27 * 简历类 */public class Resume implements Cloneable {private String name;private String sex;private String age;private WorkExperience work;public Resume(String name){this.name = name;work = new WorkExperience();}private Resume(WorkExperience work) throws CloneNotSupportedException {this.work = (WorkExperience) work.clone();}//设置个人信息public void SetPersonalInfo(String sex,String age){this.sex = sex;this.age = age;}//设置工作经历public void SetWorkExperience(String workDate,String company){work.setWorkDate(workDate);work.setCompany(company);}//显示public void Display(){System.out.println(name+"\t"+sex+"\t"+age);System.out.println("工作经历:"+work.getWorkDate()+"\t"+work.getCompany());}@Overrideprotected Object clone() throws CloneNotSupportedException {Resume obj = new Resume(this.work);obj.name = this.name;obj.sex = this.sex;obj.age = this.age;return obj;}}

提供Clone方法调用私有构造函数,以便克隆“工作经历”的数据。

在clone方法调用私有的够着方法,让“工作经历”克隆完成,然后再给这个“简历”对象的相关字段赋值,最终返回一个深复制的简历对象。

客户端代码不变,运行结果显示:

达到了我们希望三次显示结果各不相同的需求。

END

本文分享自微信公众号 - 程序员的成长之路(cxydczzl)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • caffe源码分析-InternalThread

    InternalThread封装自boost::thread的线程,主要用于多线程的数据获取(可以理解为solver前向传播的同时,后台线程继续获取下一个bat...

    bear_fish
  • [图解] 快速排序

    经典快速排序总是指定数组或者某部分的最后一个元素作为基准值,随机快速排序指定数组或者某一部分中的随机值作为基准值。

    CoderJed
  • 【SQL】小心在循环中声明变量——浅析SQL变量作用域

    如果你认为这个语句跑起来没问题,那你值得看下去,会避免以后踩到【SQL变量作用域】的坑。

    AhDung
  • caffe源码分析-DataLayer

    DataLayer作为caffe训练时的数据层(以多线程的方式读取数据加速solver的训练过程),继承自BaseDataLayer/BasePrefetchi...

    bear_fish
  • 多线程----Immutable VS Mutable (可变与不可变)

    Immutable是什么意思?不变的、不发生改变的意思。在JDK中有很多的类被设计成不可变的,举个大家经常用到的类java.lang.String,Strin...

    令仔很忙
  • Javascript:JSON总结

      JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它是基于ECMAScript的一个子集,采用完全独立于语言的文...

    王金龙
  • Java核心技术 卷I 基础知识 学习笔记(9)

    多进程与多线程有哪些区别呢?本质的区别在于每个进程拥有自己的一整套变量,而线程则共享数据。似乎有些风险,但是共享变量使线程之间的通信比进程之间的通信更有效、更容...

  • [算法题] 荷兰国旗问题

    假设这样的条纹有多条,且各种颜色的数量不一,并且随机组成了一个新的图形,新的图形可能如下图所示,但是绝非只有这一种情况:

    CoderJed
  • caffe源码分析-DataReader

    DataReader作为DataLayer的数据成员变量,以多线程的方式从数据库(如lmdb, hdf5)读取数据:

    bear_fish
  • caffe源码分析-layer_factory

    caffe中有许多的layer,在net中创建连接layer是通过工厂模式的方式创建,而不是每一个new然后连接。在net.cpp中创建layer方式如下:

    bear_fish

扫码关注云+社区

领取腾讯云代金券