设计模式之--原型模式(深浅拷贝)

原型模式

缘起

某天,小明的Leader找到小明:“小明啊,如果有个发简历的需求,就是有个简历的模板,然后打印很多份,要去一份一份展示出来,用编程怎么实现呢?”

小明一听,脑袋里就有了思路,二十分钟后给了一版代码

// 简历类
public class Resume {
    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(this.name + " " + this.sex + " " + this.age);
        System.out.println("工作经历 " + this.timeArea + " " + this.company);
    }
}

客户端代码

public static void main(String[] args) {
    Resume resume1 = new Resume("小明");
    resume1.setPersonalInfo("男", "22");
    resume1.setWorkExperience("2021-2023", "XX公司");

    Resume resume2 = new Resume("小明");
    resume2.setPersonalInfo("男", "22");
    resume2.setWorkExperience("2021-2023", "XX公司");

    Resume resume3 = new Resume("小明");
    resume1.setPersonalInfo("男", "22");
    resume1.setWorkExperience("2021-2023", "XX公司");

    resume1.display();
    resume2.display();
    resume3.display();

}

Leader看后,说道:“挺好,这其实就是我当年手写简历时代的代码哈哈哈,三份简历需要实例化三次。你觉得这样会不会麻烦呢?如果二十份简历,你就要实例化二十次是不是;而且如果你写错了一个字,那你就要改20次,你可以这么写”

public class Test {
    public static void main(String[] args) {
        Resume resume1 = new Resume("小明");
        resume1.setPersonalInfo("男", "22");
        resume1.setWorkExperience("2021-2023", "XX公司");

        Resume resume2 = resume1;

        Resume resume3 = resume1;

        resume1.display();
        resume2.display();
        resume3.display();

    }
}

“其实就是传递引用对象,而不是传值,这样做就如同是在resume2、resume3纸上是空白的,而将resume1上的内容粘贴到了resume2、resume3上面,你还有没有其他的方式能实现呢?比如emmm,Clone克隆”。

原型模式

忙活了好一会儿,小明找到了一个相关的设计模式–原型模式。

原型模式(Prototype),用原型实例指定创建对象的种类,并且通过复制这些原型创建的对象。

在这里插入图片描述

原型模式其实就是从一个对象再创建另外一个可制定的对象,而且不需要知道任何的创建细节。

看下基本原型模式的代码。

  • 原型类
// 原型类
public abstract class Prototype implements Cloneable{
    private String id;
    public Prototype(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }
    @Override
    protected Object clone() {
        Object object = null;
        try {
            object = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆异常");
        }
        return object;
    }
}
  • 具体原型类
public class ConcretePrototype extends Prototype {

    public ConcretePrototype(String id) {
        super(id);
    }
}
  • 客户端调用
ConcretePrototype p1 = new ConcretePrototype("123456");
System.out.println("原型ID:" + p1.getId());

ConcretePrototype p2 = (ConcretePrototype) p1.clone();
System.out.println("克隆ID:" + p2.getId());

这样子只需要实例化一个对象,其他的类实例化时,只需要克隆这个对象即可。

对于Java而言,那个原型抽象类Prototype是用不到的,因为克隆实在是太常用了,所以Java提供了Cloneable接口,其中有一个唯一的方法就是clone(),我们只需要实现这个接口就可以完成原型模式了。

简历原型模式实现

小明二十分钟后,第二版代码出炉了。

// 简历类
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(this.name + " " + this.age + " " + this.age);
        System.out.println("工作经历 " + this.timeArea + " " + this.company);
    }

    @Override
    protected Resume clone() throws CloneNotSupportedException {
        return (Resume) super.clone();
    }
}
  • 客户端调用
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume resume1 = new Resume("小明");
        resume1.setPersonalInfo("男", "22");
        resume1.setWorkExperience("2021-2023", "XX公司");
        Resume resume2 = resume1.clone();
        Resume resume3 = resume1.clone();

        resume1.display();
        resume2.display();
        resume3.display();
    }
}
// 结果如下
小明 男 22
工作经历 2021-2023 XX公司
小明 男 22
工作经历 2021-2023 XX公司
小明 男 22
工作经历 2021-2023 XX公司

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

Leader接着又问道:“别高兴太早了,你知道这种clone有什么弊端吗,或者说是需要注意的点呢”

小明摇了摇头,Leader接着说:“你知道深浅拷贝吧,如果字段是值类型的,则对该字段逐位复制;如果是引用类型的则只复制引用,不复制引用的对象;因此,原始对象及其副本中的引用都是同一个对象”。

“你先把工作经历单独抽离出来,然后用简历类使用它们。”

小明不到十分钟,改完了。

  • 简历类
// 简历类
public class Resume implements Cloneable{
    private String name;
    private String sex;
    private String age;


    private WorkExperience work;

    public Resume(String name) {
        this.name = name;
        this.work = new WorkExperience(); // 实例化工作经历对象
    }

    // 设置个人信息
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    // 设置工作经历
    public void setWorkExperience(String timeArea, String company) {
        this.work.setTimeArea(timeArea);
        this.work.setCompany(company);
    }

    // 展示简历
    public void display() {
        System.out.println(this.name + " " + this.sex + " " + this.age);
        System.out.println("工作经历 " + this.work.getTimeArea() + " " + this.work.getCompany());
    }

    @Override
    protected Resume clone() throws CloneNotSupportedException {
        return (Resume) super.clone();
    }
}
  • 工作经历类
public class WorkExperience {

    private String timeArea;
    private String company;

    public String getTimeArea() {
        return timeArea;
    }

    public void setTimeArea(String timeArea) {
        this.timeArea = timeArea;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}
  • 客户端
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume resume1 = new Resume("小明");
        resume1.setPersonalInfo("男", "22");
        resume1.setWorkExperience("2021-2023", "XX公司");

        Resume resume2 = resume1.clone();
        resume2.setWorkExperience("2001-2003", "ABC集团");

        Resume resume3 = resume1.clone();
        resume2.setWorkExperience("2005-2007", "ABC公司");

        resume1.display();
        resume2.display();
        resume3.display();

    }
}
// 执行结果如下
小明 男 22
工作经历 2005-2007 ABC公司
小明 男 22
工作经历 2005-2007 ABC公司
小明 男 22
工作经历 2005-2007 ABC公司

“看明白了吧,一个原型,两个副本它们的,workExperience对象全都是同一个引用,所以你改一个,其他的全都变了,这就是浅复制了。而我需要它们的workExperience对象全都是复制的对象,不能相同。”

简历深拷贝实现

“实现这个其实很简单,就是你的被引用对象,也去实现Cloneable接口,实现clone()方法,然后在引用类中将它们处理下就行了,快去查下相关资料,实现一下试试”。

小明半小时后,新的代码又出炉了。

  • WorkExperience工作经历类
public class WorkExperience implements Cloneable{

    private String timeArea;
    private String company;

    @Override
    protected WorkExperience clone() throws CloneNotSupportedException {
        return (WorkExperience) super.clone();
    }

	.....
}
  • 简历类
// 简历类
public class Resume implements Cloneable{
    private String name;
    private String sex;
    private String age;

    private WorkExperience work;

	....

    @Override
    protected Resume clone() throws CloneNotSupportedException {
        // 处理引用的对象
        Resume r = (Resume) super.clone();
        r.work = this.work.clone();
        return r;
    }
}

再来测试下。

小明 男 22
工作经历 2021-2023 XX公司
小明 男 22
工作经历 2005-2007 ABC公司
小明 男 22
工作经历 2021-2023 XX公司

总结

浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/135937.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ARM64 linux并发与同步之内存屏障

1.2 内存屏障 1.2.1 概念理解 原理部分比较苦涩难懂,我们先不过多详细介绍这部分的由来和经过,接下来着重讲解什么用途和实现; ARM64架构中提供了3条内存屏障指令。 数据存储屏障(Data Memory Barrier, DMB)指令。数据同步屏障(Data Synch…

劲松HPV防治诊疗中心发布:HPV感染全面防治解决方案

在当今社会,HPV(人乳头瘤病毒)感染问题已成为广大公众关注的焦点。作为一种高度传染性的病毒,HPV感染不仅可能导致生殖器疣,还可能引发各种癌症。面对这一严重威胁,劲松HPV防治诊疗中心以其专业的医疗团队、正规的治疗流程和全方位…

Python基础入门例程51-NP51 列表的最大与最小(循环语句)

最近的博文: Python基础入门例程50-NP50 程序员节(循环语句)-CSDN博客 Python基础入门例程49-NP49 字符列表的长度-CSDN博客 Python基础入门例程48-NP48 验证登录名与密码(条件语句)-CSDN博客 目录 最近的博文&…

函数极限求解方法归纳

1、连续函数直接代入值(加减不可以部分代入值) 例题1 配凑构造等价无穷小 等价无穷小 注意:不要在加减中部分使用等价无穷小,可以利用拆极限的方式求,拆出来的每一部分都要有极限,如果有一部分没有极限就是…

STM32F4X定时器之通用定时器

一、STM32通用定时器概述 通用定时器包括一个16位或32位自动重载计数器,可通过可编程预分频器进行驱动。定时器可以实现多种功能,包括测量输入信号的脉冲宽度和生成输出波形,通过使用定时器预分频器和RCC时钟控制器预分频器,可以…

目标检测——Yolo系列(YOLOv1/2/v3/4/5/x/6/7/8)

目标检测概述 什么是目标检测? 滑动窗口(Sliding Window) 滑动窗口的效率问题和改进 滑动窗口的效率问题:计算成本很大 改进思路 1:使用启发式算法替换暴力遍历 例如 R-CNN,Fast R-CNN 中使用 Selectiv…

C++算法:完美矩形

题目 给你一个数组 rectangles ,其中 rectangles[i] [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi, yi) ,右上顶点是 (ai, bi) 。 如果所有矩形一起精确覆盖了某个矩形区域,则返回 true ;否则&#xf…

AI 绘画 | Stable Diffusion WebUI的基本设置和插件扩展

前言 Stable Diffusion WebUI是一个基于Gradio库的浏览器界面,用于配置和生成AI绘画作品,并且进行各种精细地配置。它支持目前主流的开源AI绘画模型,例如NovelAI/Stable Diffusion。 在基本设置方面,Stable Diffusion WebUI的默…

asp.net外卖网站系统VS开发mysql数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net外卖网站系统 是一套完善的web设计管理系统,系统采用mvc模式(BLLDALENTITY)系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为mysql,使用c#语…

Git的原理与使用(一)

目录 Git初始 Git安装 Git基本操作 创建git本地仓库 配置git 工作区,暂存区,版本库 添加文件,提交文件 查看.git文件 修改文件 版本回退 小结 Git初始 git是一个非常强大的版本控制工具.可以快速的将我们的文档和代码等进行版本管理. 下面这个实例看理解下为什么需…

CountDownLatch和CyclicBarrier详解

1. CountDownLatch 1.1 简介 CountDownLatch 是 Java 中并发包(java.util.concurrent)提供的一种同步工具,用于在多线程环境中协调多个线程之间的执行顺序。它的作用是允许一个或多个线程等待其他线程完成操作。 CountDownLatch 通过一个计…

java使用geotools导出shp文件

SHP格式是一种矢量数据格式,用于存储地理信息系统(GIS)数据。 SHP文件由一系列有序的文件组成,我们导出的shp文件包括.shp、.shx、.dbf、.prj以及.fix文件。 .shp(shape)文件:存储矢量地图数据&…

自定义类型:联合和枚举

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1. 联合体 1.1 联合体类型的声明 1.2 联合体的特点 1.3 相同成员的结构体和联合体对比 1.4 联合体大小的计算 1.5 联合的一个练习 2. 枚举类型 2.1 枚举类型的声明…

思维模型 目标效应

本系列文章 主要是 分享 思维模型,涉及各个领域,重在提升认知。明确目标,激发内在动机。 1 目标效应的应用 1.1 目标效应在教育领域的应用-棉花糖实验 美国斯坦福大学心理学系的教授米歇尔(Walter Mischel)曾经进行了…

1204. 错误票据

题目: 1204. 错误票据 - AcWing题库 思路: 将输入的数据存入数组,从小到大排序后遍历,若 (a[i] a[i - 1])res1 a[i]--->重号;若(a[i] - a[i - 1] > 2)res2 a[i] - 1--->断号。 难点:题目只告诉我们输入…

1977 智慧校园平台开发与实现JSP【程序源码+文档+调试运行】

摘要 本文旨在设计和实现一个智慧校园平台系统,以满足管理员、教师和学生三类用户的需求。管理员拥有最高管理权限,可对教师和学生用户进行管理;教师用户可查看和管理本班学生信息、成绩等;学生用户可查看个人信息、考试成绩等。…

Leetcode139单词拆分及其多种变体问题

1 声明 1.1 首先,大家常常把这道题当作了背包问题来做,因为其循环结构和背包问题刚好相反,但事实如此嘛? 背包问题通常都是组合问题,这其实是一道“”面向目标值的排列问题“,具体和背包问题有什么不同可…

[01]汇川IMC30G-E系列运动控制卡应用笔记

简介 IMC30G-E系列产品是汇川技术自主研制的高性能EtherCAT网络型运动控制器(卡),同时兼容脉冲轴的控制;IMC30G-E支持点位/JOG、插补、多轴同步、高速位置比较输出、PWM等全面的运动控制功能,具备高同步控制精度。 开发…

【Nginx】深入浅出搞懂Nginx

Nginx是一款轻量级的Web服务器、反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用。 反向代理服务器? 经常听人说到一些术语,如反向代理,那么什么是反向代理&a…

Pinia 状态管理器 菠萝:Option Store风格

Pinia介绍: Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。 Pinia 大小只有 1kb 左右,超轻量级,你甚至可能忘记它的存在! 相比 Vuex,Pinia 的优点: 更贴合 Vue 3 的 Composition API 风…