原型模式(七)

不管怎么样,都要继续充满着希望

上一章简单介绍了抽象工厂模式(六), 如果没有看过,请观看上一章

一. 原型模式

引用 菜鸟教程里面的原型模式介绍: https://www.runoob.com/design-pattern/prototype-pattern.html

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,

它提供了一种创建对象的最佳方式之一。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。

例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,

在需要的时候更新数据库,以此来减少数据库调用

一.一 介绍

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

主要解决:在运行期建立和删除原型。

何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

关键代码: 1、实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。

优点: 1、性能提高。 2、逃避构造函数的约束。

缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。

使用场景
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,
可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,
然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

image-20230614102228392

二. 原型模式实现

定义一个对象 Sheep

@Data
public class Sheep {

    private Integer id;
    private String name;

}

二.一 未使用原型模式

测试方法:

@Test
    public void oneTest (){

        Sheep sheep = new Sheep();
        sheep.setId(1);
        sheep.setName("羊");

        Sheep newSheep = sheep;
        log.info("新的 {}", newSheep);
        newSheep.setName("名称改变了:");
        log.info("改变后的名称: {}", newSheep);
        log.info("之前老的数据: {}", sheep);
    }

image-20230614102446896

会将原先的老对象也改变。

重新构建对象, new Sheep() 的话

   @Test
    public void twoTest (){

        Sheep sheep = new Sheep();
        sheep.setId(1);
        sheep.setName("羊");

        Sheep newSheep = new Sheep();
        newSheep.setId(sheep.getId());
        newSheep.setName(sheep.getName());
        log.info("新的 {}", newSheep);
        newSheep.setName("名称改变了:");
        log.info("改变后的名称: {}", newSheep);
        log.info("之前老的数据: {}", sheep);
    }

image-20230614102610278

不会改变老的, 但每次都是新 new 对象, 需要将每一个属性都放置进去, 太复杂.

二.二 实现 Cloneable 接口,复制对象

@Data
public class ThreeSheep implements Cloneable{

    private Integer id;
    private String name;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

测试方法:

@Test
    public void threeTest () throws Exception{

        ThreeSheep sheep = new ThreeSheep();
        sheep.setId(1);
        sheep.setName("羊");

        ThreeSheep newSheep = (ThreeSheep) sheep.clone();
        log.info("新的 {}", newSheep);
        newSheep.setName("名称改变了:");
        log.info("改变后的名称: {}", newSheep);
        log.info("之前老的数据: {}", sheep);
    }

通过 .clone() 方法 将 原先的对象进行复制, 这就是原型模式.

image-20230614102916952

二.三 深复制,浅复制

定义一个 User 对象 和 Dept 对象, User 里面依赖 Dept 对象

二.三.一 浅复制

User

@Data
public class User implements Cloneable {
    private Integer id;
    private String name;
    private Dept dept;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Dept.java

@Data
public class Dept implements Cloneable{
    private Integer id;
    private String name;

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

测试方法:

    @Test
    public void fourTest() throws Exception {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        Dept dept = new Dept();
        dept.setId(1);
        dept.setName("研发部");
        user.setDept(dept);

        log.info("构建用户: {}",user);
        User cloneUser = (User) user.clone();
        cloneUser.setName("张三Copy");
        log.info("克隆后的用户: {}",cloneUser);
        log.info("之前的用户: {}",user);

        // 会发现 , Dept 同时改变了, 即 Dept 并没有 Copy 成功。
        cloneUser.getDept().setName("信息部");

        log.info("修改后的用户: {}",cloneUser);

        log.info("之前的用户: {}",user);
    }

image-20230614103313247

可以发现,会将 Dept 引用的对象 也进行修改了。

浅复制即 只复制了当前对象的普通属性, 并没有将引用属性也进行复制。

二.三.二 深复制

@Data
public class NewDept implements Cloneable{
    private Integer id;
    private String name;

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

注意 clone() 方法内的写法, 先进行 dept 的 clone() 再设置到 dept 属性里面。

@Data
public class NewUser implements Cloneable {
    private Integer id;
    private String name;
    private NewDept dept;

    @Override
    public Object clone() throws CloneNotSupportedException {
        NewUser newUser = (NewUser) super.clone();
        NewDept deptCopy = (NewDept) dept.clone();
        newUser.setDept(deptCopy);
        return newUser;
    }
}

测试方法:

@Test
    public void fiveTest() throws Exception {
        NewUser user = new NewUser();
        user.setId(1);
        user.setName("张三");
        NewDept dept = new NewDept();
        dept.setId(1);
        dept.setName("研发部");
        user.setDept(dept);

        log.info("构建用户: {}",user);
        NewUser cloneUser = (NewUser) user.clone();
        cloneUser.setName("张三Copy");
        log.info("克隆后的用户: {}",cloneUser);
        log.info("之前的用户: {}",user);

        cloneUser.getDept().setName("信息部");

        log.info("修改后的用户: {}",cloneUser);

        log.info("之前的用户: {}",user);
    }

image-20230614103610992

可以发现, dept 对象并没有改变, user.clone() 方法 将 user 普通属性 和 dept 对象都进行了复制。

二.四 序列化实现深复制

需要实现 Serializable 接口

@Data
public class NewDept2 implements Serializable{
    private Integer id;
    private String name;
}
@Data
public class NewUser2 implements Serializable {
    private Integer id;
    private String name;
    private NewDept2 dept;

    /**
      深复制
     */
    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);

            NewUser2 copyObj = (NewUser2)ois.readObject();
            return copyObj;

        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            }catch (Exception e) {

            }
        }
    }
}

测试方法:

 @Test
    public void sexTest(){
        NewUser2 user = new NewUser2();
        user.setId(1);
        user.setName("张三");
        NewDept2 dept = new NewDept2();
        dept.setId(1);
        dept.setName("研发部");
        user.setDept(dept);

        log.info("构建用户: {}",user);
        NewUser2 cloneUser = (NewUser2) user.deepClone();
        cloneUser.setName("张三Copy");
        log.info("克隆后的用户: {}",cloneUser);
        log.info("之前的用户: {}",user);

        cloneUser.getDept().setName("信息部");

        log.info("修改后的用户: {}",cloneUser);

        log.info("之前的用户: {}",user);
    }

image-20230614104034309

是实现了深复制的.

二.五 通过第三方工具类 实现深复制

如通过 hutool 的 ObjectUtil.clone 实现

NewUser2 和 NewDept2 与上面 二.四 的一样,都需要实现序列化接口

    @Test
    public void sevenTest() {
        NewUser2 user = new NewUser2();
        user.setId(1);
        user.setName("张三");
        NewDept2 dept = new NewDept2();
        dept.setId(1);
        dept.setName("研发部");
        user.setDept(dept);

        log.info("构建用户: {}",user);
        // 通过第三方对象, 需要序列化接口。
        NewUser2 cloneUser = ObjectUtil.clone(user);
        cloneUser.setName("张三Copy");
        log.info("克隆后的用户: {}",cloneUser);
        log.info("之前的用户: {}",user);
        
        cloneUser.getDept().setName("信息部");

        log.info("修改后的用户: {}",cloneUser);

        log.info("之前的用户: {}",user);
    }

深复制也是成功的。

image-20230614104219576


本章节的代码放置在 github 上:


https://github.com/yuejianli/DesignPattern/tree/develop/Prototype


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

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

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

相关文章

Camera | 11.瑞芯微摄像头采集图像颜色偏绿解决笔记

前言 在实际调试基于瑞芯微平台的camera过程中,发现显示的图片发绿, 现在把调试步骤分享给大家: 1、修改iq文件 sdk中位置: external/camera_engine_rkaiq/iqfiles/isp21/ov13850_ZC-OV13850R2A-V1_Largan-50064B31.xml【现在…

QT基础教程之一创建Qt项目

QT基础教程1创建Qt项目 根据模板创建 打开Qt Creator 界面选择 New Project或者选择菜单栏 【文件】-【新建文件或项目】菜单项 弹出New Project对话框,选择Qt Widgets Application 选择【Choose】按钮,弹出如下对话框 设置项目名称和路径,…

javaScript蓝桥杯---用什么来做计算

目录 一、介绍二、准备三、目标四、代码五、完成 一、介绍 古以算盘作为计算工具。算盘常为木制矩框,内嵌珠子数串,定位拨珠,可做加减乘除等运算。站在前人的肩膀上,后人研究出计算器,便利了大家的生活,我…

element-plus布局排版问题总结(更新ing)

文章目录 el-container空隙修改app组件 el-header容器空隙检查前端修改el-headerel-container el-container空隙 源码-更改了容器的显示&#xff0c;占满屏幕 <template><div class"common-layout"><el-container><el-header><el-row cl…

SpeechGen:用提示解锁语音语言模型(Speech LM)的生成能力

论文链接&#xff1a; https://arxiv.org/pdf/2306.02207.pdf Demo: https://ga642381.github.io/SpeechPrompt/speechgen.html Code: https://github.com/ga642381/SpeechGen 引言与动机 大型语言模型 &#xff08;LLMs&#xff09;在人工智能生成内容&#xff08;AIGC…

微服务技术简介

微服务技术简介 服务架构的演变微服务架构的常见概念微服务常见的解决方案Spring CloudSpring Cloud Alibaba微服务技术对比常用的微服务组件 微服务架构图 服务架构的演变 单体架构&#xff1a;当一个系统业务量很小的时候&#xff0c;将业务的所有功能集中在一个项目中开发&…

STM32速成笔记—串口通信

文章目录 一、什么是串口通讯二、串口通讯有什么用三、STM32的串口通信四、串口通信相关概念4.1 波特率4.2 全双工和半双工4.3 同步通信和异步通信 五、硬件连接六、串口通讯程序配置6.1 使能串口时钟和GPIO时钟6.2 初始化GPIO6.3 初始化串口参数6.4 使能串口6.5 串口接收中断6…

用python写网络爬虫

第二章 数据抓取 首先 &#xff0c; 我们会介绍一个叫 做Firebug Lite 的浏览器扩展&#xff0c; 用 于检查网页 内容 &#xff0c; 如 果你有一些网络开发背景的话&#xff0c; 可能 己经对该扩展十分熟悉 了 。 然后 &#xff0c;我们会介绍三 种抽取网 页数据的 方法 &…

java【抽象类与接口】

抽象类与接口 1 抽象类1.1 定义与使用1.2 抽象类和抽象方法使用原则 2 接口2.1 定义2.2 使用规则 3. JDK中内置接口3.1 Comparable接口3.2 Cloneable接口 抽象类与接口的对比 前言&#xff1a;如果强制要求子类必须覆写一些方法&#xff0c;则就会用到抽象类和抽象方法 1 抽象类…

使用 MATLAB 进行气象激光雷达图像分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

STM32——01-开发软件Keil5及STM32CubeMX的安装

1.1开发环境的安装 编程语言&#xff1a;C语言 需要安装的软件有两个&#xff1a;Keil5和STM32CubeMX 安装 安装包&#xff08;不需要太新&#xff0c;以 MDK324 为例&#xff0c;最新的 MDK327 有问题&#xff09; 安装过程一路下一步即可&#xff08;建议不要安装在 C …

2023-06-09:什么是Redis事务?原理是什么?

2023-06-09&#xff1a;什么是Redis事务&#xff1f;原理是什么&#xff1f; 答案2023-06-09&#xff1a; Redis中的事务是以一组命令的形式出现的&#xff0c;这些命令被认为是最小的执行单位。事务可以保证在一个单独独立的隔离操作中执行所有命令&#xff0c;而且所有命令…

深度研究微软的资产负债表和财务状况以及未来投资价值

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 微软股票的关键指标 猛兽财经认为&#xff0c;微软公布的2023财年第三季度财务业绩&#xff0c;有三个关键指标值得投资者关注。 第一个关键指标是利息收入。微软的利息收入目前已经同比增长了44%&#xff0c;从2022财年第…

从搭建hadoop开始学习大数据中分而治之的MapReduce(伪集群模式)

环境准备 首先需要将如下四个必要的文件下载到计算机&#xff08;已经附上了下载地址&#xff0c;点击即可下载&#xff09;。 Vmware Workstation 17.x 【官方的下载地址】 CentOS-7-x86_64-Minimal-2009【阿里云镜像站下载地址】 openjdk-8u41-b04-linux-x64-14_jan_2020【开…

踏上极速向未来之旅!——2023年量化科技嘉年华·专场回顾

2023年6月2日—3日&#xff0c;“2023量化科技嘉年华”在上海世博中心召开&#xff0c;这场为期两天的量化科技盛会&#xff0c;多方位呈现了量化科技创新成果&#xff0c;吸引了众多关注。 在嘉年华的第二天&#xff0c;多场主题论坛同步进行。 由华锐技术主办的“极速向未来…

ChatGPT 4 的 6 个最佳使用场景

作者&#xff1a;SYDNEY BUTLER 译者&#xff1a;明明如月 无论是在 ChatGPT 中还是通过 API&#xff0c;对 OpenAI 的 GPT-4 模型的访问比 GPT-3.5 限制更多。这意味着你需要慎重考虑在何种情况下使用 GPT-4&#xff0c;并选择性地将最适合的任务交给它&#xff0c;以便让其发…

短视频seo源代码部署步骤

一、 部署短视频SEO矩阵系统源代码&#xff0c;您需要遵循以下步骤&#xff1a; 准备服务器环境 首先&#xff0c;您需要准备一个服务器环境来托管源代码。您可以选择云服务器&#xff08;例如AWS&#xff0c;阿里云等&#xff09;或自己的私人服务器。 安装所需软件 在服务器…

Hbase

Hbase 思考环节&#xff1a; 1.什么是hbase 1.1简介 HBase 是一个面向列式存储的分布式数据库&#xff0c;其设计思想来源于 Google 的 BigTable 论文。 HBase 底层存储基于 HDFS 实现&#xff0c;集群的管理基于 ZooKeeper 实现。 HBase 良好的分布式架构设计为海量数据的…

图神经网络 GNN 入门

参考链接 A Gentle Introduction to Graph Neural Networks (distill.pub) 零基础多图详解图神经网络&#xff08;GNN/GCN&#xff09;【论文精读】_哔哩哔哩_bilibili 目录 图的基本构成 图的表示方法 图的示例 图网络的基本任务 图网络的处理 影响图网络效果的超参数 …

快速创建Django项目

环境搭建 安装Django之前需要先安装python环境和pycharm。 目前 Django 1.6.x 以上版本已经完全兼容 Python 3.x。 python安装教程&#xff1a;https://blog.csdn.net/David_house/article/details/100110542 项目创建 打开pycharm&#xff0c;选择文件–>新建项目 创建D…
最新文章