设计模式-原型模式

原型模式


文章目录

  • 原型模式
  • 什么是原型模式
  • 为什么要用原型模式
  • 原型模式的实现方式
    • 浅拷贝
    • 深拷贝
  • 如何实现深拷贝
    • 递归拷贝对象
    • 序列化与反序列化
    • 优化浅拷贝与深拷贝
  • 总结


什么是原型模式

  如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。


为什么要用原型模式

  实际上,创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。
  但是,如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。


原型模式的实现方式

  我们现在有一个OrganizationInfoMap(一个ConcurrentHashMap<String, Organization>),存储的是系统内所有组织机构id-组织机构信息的对应关系,每个组织机构都有最后修改时间(lastModifyTime),OrganizationInfoMap也有两个属性,一个是版本V一个是时间T,即某个版本对应的最新的组织机构最后修改时间。现在有一个需求,30万组织机构在这个OrganizationInfoMap中,要实现定时更新,如果每次定时都创建新的30万终端,那么耗时非常长。我们可以将OrganizationInfoMap复制一份,然后筛选最后修改时间大于T的组织机构更新到OrganizationInfoMap里。代码示例如下:

public class OrgRefreshService {
    private HashMap<String, Organization> organizationInfoMap = new HashMap<>();
    private long lastModifyTime = -1;

    public void refresh() {
        HashMap<String, Organization> organizationInfoMapTmp = (HashMap<String, Organization>) organizationInfoMap.clone();

        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到currentKeywords中
        List<Organization> toBeUpdatedOrganizations = getOrganization(lastModifyTime);
        long maxNewUpdatedTime = lastModifyTime;
        for (Organization organization : toBeUpdatedOrganizations) {
            if (organization.getLastModifyTime() > maxNewUpdatedTime) {
                maxNewUpdatedTime = organization.getLastModifyTime();
            }
            if (organizationInfoMapTmp.containsKey(organization.getId())) {
                Organization organizationOld = organizationInfoMapTmp.get(organization.getId());
                organizationOld.setLastModifyTime(organization.getLastModifyTime());
            } else {
                organizationInfoMapTmp.put(organization.getId(), organization);
            }
        }

        lastModifyTime = maxNewUpdatedTime;
        organizationInfoMap = organizationInfoMapTmp;
    }

    private List<Organization> getOrganization(long lastModifyTime) {
        // TODO: 从数据库中取出更新时间>lastModifyTime的数据
        return null;
    }
}

  当我们有一个新的需求,organizationInfoMap中的数据要么全是新的,要是全是旧的,不能有混杂的数据(即toBeUpdatedOrganizations中的数据不能存在部分更新到organizationInfoMapTmp中的情况,要么全都更新过去,要么全都没更新)。
  那么上面的代码肯定是不满足的,为什么呢?在讲解原因之前,我们先介绍下两个概念:浅拷贝和深拷贝。


浅拷贝

  浅拷贝只会拷贝对象地址的引用,不会在内存中开辟新的空间保存新的对象;图示如下:
在这里插入图片描述


深拷贝

  深拷贝会在内存中开辟新的空间保存新的对象;图示如下:
在这里插入图片描述
  现在来看为什么上面的代码不能满足要么全都是最新的,要么全都是老的。因为在 Java 语言中,Object 类的 clone() 方法执行的就是我们刚刚说的浅拷贝。它只会拷贝对象中的基本数据类型的数据(比如,int、long),以及引用对象(Organization)的内存地址,不会递归地拷贝引用对象本身。
  在上面的代码中,我们通过调用 HashMap 上的 clone() 浅拷贝方法来实现原型模式。当我们通过 organizationInfoMapTmp更新 Organization对象的时候,organizationInfoMapTmp和 organizationInfoMap因为指向相同的一组 Organization对象,就会导致 organizationInfoMap中指向的 Organization,有的是老版本的,有的是新版本的,就没法满足我们之前的需求:organizationInfoMap中的数据在任何时刻都是同一个版本的,不存在介于老版本与新版本之间的中间状态。


如何实现深拷贝

递归拷贝对象

  递归拷贝对象、对象的引用对象以及引用对象的引用对象……直到要拷贝的对象只包含基本数据类型数据,没有引用对象为止。根据这个思路对之前的代码进行重构。重构之后的代码如下所示:

public class OrgRefreshService {
    private HashMap<String, Organization> organizationInfoMap = new HashMap<>();
    private long lastModifyTime = -1;

    public void refresh() {
        HashMap<String, Organization> organizationInfoMapTmp = new HashMap<>();

        for (HashMap.Entry e : organizationInfoMap.entrySet()) {
            Organization organization = (Organization) e.getValue();
            Organization newOrganization = new Organization(organization.getId(), organization.getLastModifyTime());
            organizationInfoMapTmp.put(organization.getId(), newOrganization);
        }

        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到currentKeywords中
        List<Organization> toBeUpdatedOrganizations = getOrganization(lastModifyTime);
        long maxNewUpdatedTime = lastModifyTime;
        for (Organization organization : toBeUpdatedOrganizations) {
            if (organization.getLastModifyTime() > maxNewUpdatedTime) {
                maxNewUpdatedTime = organization.getLastModifyTime();
            }
            if (organizationInfoMapTmp.containsKey(organization.getId())) {
                Organization organizationOld = organizationInfoMapTmp.get(organization.getId());
                organizationOld.setLastModifyTime(organization.getLastModifyTime());
            } else {
                organizationInfoMapTmp.put(organization.getId(), organization);
            }
        }

        lastModifyTime = maxNewUpdatedTime;
        organizationInfoMap = organizationInfoMapTmp;
    }

    private List<Organization> getOrganization(long lastModifyTime) {
        // TODO: 从数据库中取出更新时间>lastModifyTime的数据
        return null;
    }
}

序列化与反序列化

  先将对象序列化,然后再反序列化成新的对象。具体的示例代码如下所示:

public Object deepCopy(Object object) {
  ByteArrayOutputStream bo = new ByteArrayOutputStream();
  ObjectOutputStream oo = new ObjectOutputStream(bo);
  oo.writeObject(object);
  
  ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
  ObjectInputStream oi = new ObjectInputStream(bi);
  
  return oi.readObject();
}


优化浅拷贝与深拷贝

  刚刚的两种实现方法,不管采用哪种,深拷贝都要比浅拷贝耗时、耗内存空间。针对我们这个应用场景,有没有更快、更省内存的实现方式呢? 我们可以先采用浅拷贝与深拷贝混合的方式只对要更新的对象完成深拷贝,代码示例如下:

public class OrgRefreshService {
    private HashMap<String, Organization> organizationInfoMap = new HashMap<>();
    private long lastModifyTime = -1;

    public void refresh() {
        HashMap<String, Organization> organizationInfoMapTmp = (HashMap<String, Organization>) organizationInfoMap.clone();

        // 从数据库中取出更新时间>lastUpdateTime的数据,放入到currentKeywords中
        List<Organization> toBeUpdatedOrganizations = getOrganization(lastModifyTime);
        long maxNewUpdatedTime = lastModifyTime;
        for (Organization organization : toBeUpdatedOrganizations) {
            if (organization.getLastModifyTime() > maxNewUpdatedTime) {
                maxNewUpdatedTime = organization.getLastModifyTime();
            }
            if (organizationInfoMapTmp.containsKey(organization.getId())) {
                organizationInfoMapTmp.remove(organization.getId());
            }
            organizationInfoMapTmp.put(organization.getId(), organization);
        }

        lastModifyTime = maxNewUpdatedTime;
        organizationInfoMap = organizationInfoMapTmp;
    }

    private List<Organization> getOrganization(long lastModifyTime) {
        // TODO: 从数据库中取出更新时间>lastModifyTime的数据
        return null;
    }
}

总结

  在实际开发工作中,原型模式的使用场景不多,但是要了解其原理,并且在有类似需求的时候能快速给出合适的方案。

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

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

相关文章

java基于mvc的停车收费系统mysql

系统需要解决的主要问题有&#xff1a; (1)车位管理模块 添加车位、查看车位状态、车位信息查询等。 (2)客户信息管理模块 客户基本信息录入、客户信息查询等。 (3)卡业务办理 添加卡信息、查余额查询、卡充值。 (4)车辆信息管理模块 车牌信息录入等。 (5)收费管理 可以调整相应…

通过工具生成指定 类型 大小 文件

今天给大家介绍一个神器 首先 大家在开发过程中或许经常需要涉及到文件上传类的功能 需要测试文件过大 空文件等等清空 不同大小的文件 而这种文件大小是比较不好控制的 但大家可以下载我的资源 文件生成工具(可生成指定大小 类型文件) 下载下来里面就有一个 fileGeneration…

开发常用的 Linux 命令1(文件和目录)

开发常用的 Linux 命令汇总1&#xff08;文件和目录&#xff09; 作为开发者&#xff0c;Linux是我们必须掌握的操作系统之一。因此&#xff0c;在编写代码和部署应用程序时&#xff0c;熟练使用Linux命令非常重要。这些常用命令不得不会&#xff0c;掌握这些命令&#xff0c;…

干货分享 | 如何在业余时间学习数据分析?

从社会学背景的数分小白到独揽公司市场部的数据分析&#xff0c;和大家简单分享我的数据分析升级打怪之路。 简单说就是两点【培养数据分析思维】【提升数据分析技能】 思维的培养主要靠书籍和课程 以下是一些对我影响较大的书籍&#xff0c;按照【入门、进阶、高阶】简单做了…

Vue2-黑马(十)

目录&#xff1a; &#xff08;1&#xff09;vuex-actions &#xff08;2&#xff09;vuex-调用actions &#xff08;3&#xff09;vue2实战-项目搭建 &#xff08;1&#xff09;vuex-actions 有这么一个需求&#xff0c;绿色的组件从服务器获取数据放入store&#xff0c;主…

图片英文翻译成中文转换器-中文翻译英文软件

您正在准备一份重要的英文资料或文件&#xff0c;但是您还不是很熟练地掌握英文&#xff0c;需要翻译才能完成您的任务吗&#xff1f;哪个软件能够免费把英文文档翻译成中文&#xff1f;让我们带您了解如何使用我们的翻译软件来免费翻译英文文档为中文。 我们的翻译软件是一款功…

grep命令详解(如何匹配多个字符串)(grep指令)(grep -q)

文章目录 20221011 grep如何匹配多个标志&#xff1f;20230412 grep -q&#xff08;Use grep -q instead of comparing output with [ -n .. ].警告&#xff09;20230421 匹配前后空格\s 参考文章&#xff1a;grep命令详解 | grep -c [被搜索文本] # 统计被搜索文本出现的次数…

冯诺依曼体系结构

冯诺依曼体系结构 目录 冯诺依曼体系结构引入1、冯诺依曼体系结构1.1 内存1.2 操作系统预加载 2、操作系统2.1 理解管理2.2 系统调用接口2.3 操作系统四大基本功能 引入 冯诺依曼体系结构&#xff08;von Neumann architecture&#xff09;是现代计算机体系结构的基础&#xf…

JavaSE学习进阶day03_03 Object类

第三章 Object类 介绍这个类之前&#xff0c;先回顾一下API&#xff1a; 3.1 概述 java.lang.Object类是Java语言中的根类&#xff0c;即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候&#xff0c;最终找的父类就是Object。 如果一个类没有特别指定父…

手势控制的机器人手臂

将向你展示如何构建机械手臂并使用手势和计算机视觉来控制它。下面有一个在开发阶段的机械手臂的演示视频。 展示开发中的手臂的演示视频&#xff1a;https://youtu.be/KwiwetZGv0s 如图所示&#xff0c;该过程首先用摄像头捕捉我的手及其标志。通过跟踪特定的界标&#xff0c;…

C++语法(16)---- 多态

https://blog.csdn.net/m0_63488627/article/details/130106690?spm1001.2014.3001.5501https://blog.csdn.net/m0_63488627/article/details/130106690?spm1001.2014.3001.5501 目录 1. 多态的概念 2.多态的实现 1.虚函数 2.多态条件 得到的多态条件 特殊条件 3.虚函…

数据结构入门-10-AVL

文章目录 一、AVL的性质1.2 平衡二叉树定义 二、添加需达到平衡2.1 平衡因子2.1.2 平衡因子的实现 2.2 判断该二叉树是否为平衡二叉树2.3 左旋右旋2.3.1 左旋LL右旋RR基本原理2.3.2 LR RLLRRL 三、AVL中删除 一、AVL的性质 平衡二叉树 AVL树得名于它的俄罗斯发明者G. M. Adels…

被裁员了,要求公司足额补缴全部公积金,一次补了二十多万!网友兴奋了,该怎么操作?...

被裁员后&#xff0c;能要求公司补缴公积金吗&#xff1f; 一位网友问&#xff1a; 被裁员了&#xff0c;要求公司把历史公积金全部足额缴纳&#xff0c;现在月薪2.3万&#xff0c;但公司每个月只给自己缴纳300元公积金&#xff0c;结果一次补了二十多万&#xff0c;一次性取出…

Node 【Buffer 与 Stream】

文章目录 &#x1f31f;前言&#x1f31f;Buffer&#x1f31f; Buffer结构&#x1f31f; 什么时候用Buffer&#x1f31f; Buffer的转换&#x1f31f; Buffer使用&#x1f31f; 创建Buffer&#x1f31f; 字符串转Buffer&#x1f31f; Buffer转字符串&#x1f31f; 拼接Buffer&am…

Java每日一练(20230417)

目录 1. N 皇后 &#x1f31f;&#x1f31f;&#x1f31f; 2. 搜索二维矩阵 &#x1f31f;&#x1f31f; 3. 发奖金问题 &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 …

权限控制_SpringSecurity

认证-授权 认证&#xff1a;系统提供的用于识别用户身份的功能&#xff0c;通常提供用户名和密码进行登录其实就是在进行认证&#xff0c;认证的目的是让系统知道你是谁。 授权&#xff1a;用户认证成功后&#xff0c;需要为用户授权&#xff0c;其实就是指定当前用户可以操作…

Node 内置模块 【fs模块】

文章目录 &#x1f31f;前言&#x1f31f;fs模块&#x1f31f; 使用fs模块&#x1f31f; 异步编程和同步编程&#x1f31f; 异步编程&#x1f31f; 同步编程 &#x1f31f;常用操作&#x1f31f; 文件操作&#x1f31f; readFile异步读取文件&#x1f31f; readFileSync同步读取…

YOLOv8 更换主干网络之 GhostNetV2

《GhostNetV2:Enhance Cheap Operation with Long-Range Attention》 轻量级卷积神经网络(CNN)是专门为在移动设备上具有更快推理速度的应用而设计的。卷积操作只能捕捉窗口区域内的局部信息,这防止了性能的进一步提高。将自注意力引入卷积可以很好地捕捉全局信息,但这将大…

【系统集成项目管理工程师】项目进度管理

&#x1f4a5;十大知识领域&#xff1a;项目进度管理 主要考计算题 项目进度管理包括以下 7 个过程: 规划进度管理过程定义活动过程排列活动顺序过程估算活动资源过程估算活动持续时间过程制定进度计划过程控制进度过程 一、规划进度管理过程 制定政策、程序和文档以管理项目进…

JeecgBoot 3.5.1 版本发布,开源的企业级低代码平台

项目介绍 JeecgBoot是一款企业级的低代码平台&#xff01;前后端分离架构 SpringBoot2.x&#xff0c;SpringCloud&#xff0c;Ant Design&Vue3&#xff0c;Mybatis-plus&#xff0c;Shiro&#xff0c;JWT 支持微服务。强大的代码生成器让前后端代码一键生成! JeecgBoot引领…