【创建者模式】原型模式

news2025/3/3 4:37:37

文章目录

    • 优秀借鉴
    • 1、简介
    • 2、结构
    • 3、浅拷贝和深拷贝
    • 4、浅拷贝实现
      • 4.1、实现步骤
      • 4.2、结果分析
    • 5、深拷贝思路
      • 5.1、clone方法
      • 5.2、序列化与反序列化
    • 6、应用场景

优秀借鉴

什么是原型模式(Prototype)?应用场景是什么?

【设计模式】原型模式 ( 概念简介 | 使用场景 | 优缺点 | 基本用法 )

设计模式 - Prototype 原型模式

1、简介

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。这种类型的设计模式属于创建型模式,用于创建重复对象的同时又能保证性能,是创建对象的最佳方式之一。

优点为性能高,简单

  • 性能高: 使用原型模式复用的方式创建实例对象,比使用构造函数重新创建对象性能要高 ; (针对类实例对象开销大的情况)
  • 流程简单: 原型模式可以简化创建的过程,可以直接修改现有的对象实例的值 , 达到复用的目的 ; (针对构造函数繁琐的情况)

缺点为实现复杂,坑多

  • 覆盖 clone 方法(必须): 必须重写对象的 clone 方法,Java 中提供了 cloneable 标识该对象可以被拷贝,但是必须覆盖 Object 的 clone 方法才能被拷贝;
  • 深拷贝与浅拷贝风险: 克隆对象时进行的一些修改,容易出错;需要灵活运用深拷贝与浅拷贝操作;

2、结构

原型模式包含如下角色:

  • 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象

image-20230212182432645

3、浅拷贝和深拷贝

在实现原型模式前,需要了解浅拷贝和深拷贝的概念:

  • 浅拷贝:当拷贝对象只包含简单的数据类型比如int、float 或者不可变的对象(字符串)时,就直接将这些字段复制到新的对象中。而引用类型的成员变量并没有复制,而是将引用对象的地址复制一份给克隆对象;

image-20230212190814693

  • 深拷贝:不管拷贝对象里面简单数据类型还是引用对象类型都是会完全的复制一份到新的对象中。

image-20230212190831554

举个不太恰当的栗子,有一个大箱子(原型对象),里面放着多个小箱子(成员变量), 小箱子里面放着不同的货物(成员变量值)。

  • 浅拷贝就是不管三七二十一,就是拿一个新的大箱子(克隆对象),将里面的小箱子(成员变量)直接复制过来得到一模一样的小箱子(地址相同);
  • 深拷贝就是大箱子(克隆对象)是新的,连里面的小箱子(成员变量)都是新的,最终得到的新大箱子(克隆对象)的小箱子数和小箱子里面的货物都是一样的,但已经是不同的箱子了(地址不同)。

4、浅拷贝实现

最简单与常用的便是浅拷贝,以给三好学生发奖状为例

4.1、实现步骤

  1. 原型类实现Cloneable接口并重写clone方法:
@Data 
public class Citation implements Cloneable {

    /** 学期 */
    private int semester;

    /** 学校 */
    private String school;

    /** 时间戳 */
    private Long timestamp;

    private Student stu;

    public Citation() {
        System.out.println("正在创建奖状……");
    }

    void show() {
        System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
        System.out.println(school + "于" + timestamp + "颁发");
    }

    @Override
    public Citation clone() {
        try {
            return (Citation) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

@Data
class Student {
    private String name;
    private String address;

    public Student(String name, String address) {
        this.name = name;
        this.address = address;
    }
}
  1. 测试类
public class CitationTest {
    public static void main(String[] args) {
        // 生成原型奖状
        Citation c1 = new Citation();
        c1.setStu(new Student("张三", "西安"));
        c1.setSchool("牛马大学");
        c1.setSemester(1);
        c1.setTimestamp(System.currentTimeMillis());

        // 复制奖状
        Citation c2 = c1.clone();

        // 分别调用show方法
        System.out.println("===== c1的show =====");
        c1.show();
        System.out.println("===== c2的show =====");
        c2.show();
        System.out.println("===== ======= =====");

        // 比较原型与克隆对象地址
        System.out.println("比较原型与克隆对象地址:" + (c1 == c2));

        // 比较成员属性地址
        System.out.println("比较学生成员属性的地址(Student):\t" + (c1.getStu() == c2.getStu()));
        System.out.println("比较学校成员属性的地址(String):\t" + (c1.getSchool() == c2.getSchool()));
        System.out.println("比较学期成员属性的地址(int):\t" + (c1.getSemester() == c2.getSemester()));
        System.out.println("比较时间成员属性的地址(Long):\t" + (c1.getTimestamp() == c2.getTimestamp()));
    }
}
  1. 运行结果

image-20230212202931008

4.2、结果分析

  1. 构造器

在克隆生成c2时并没有调用构造器,只有在手动创建c1时才会调用构造器,这证明这是使用了原型模式复用的方式创建实例对象。

  1. 原型对象与克隆对象比较

通过比较,这两个对象的地址并不相同,这证明虽然复制过程没有调用构造器,但是生成的对象是一个全新的实例,在栈中有属于自己的地址。

  1. 引用类型比较

在奖状类中定义了三种实际应用中常遇到的类型,分别是自定义引用类型、字符串类型和包装类,但是通过比较,原型对象和拷贝对象的引用类型为同一个对象,共用一个地址。

  1. 普通数据类型比较

这里还设置了一个int类型的普通数据类型,由于普通类型是将值直接赋值到新的拷贝对象中,因此这里的==比较的是两者的值。

大家可以尝试一下修改c2或c1中的某个引用类型再调用show方法进行查看,对比两者的数据变化,这可能对这一部分的理解会更深刻。

5、深拷贝思路

5.1、clone方法

经过上述浅拷贝的介绍,我们知道在实现原型模式最重要的便是实现Cloneable接口,重写clone方法,因此我们可以在重写clone方法时,再将成员变量里的引用类型变量进行克隆(前提是这一引用类型支持拷贝)。

5.2、序列化与反序列化

正是可以破坏单例因素之一的序列化与反序列化,通过对象流将对象输出后再写入,这种方式生成的拷贝对象便是深拷贝后的实例对象。

针对上面的两种写法其实都是可以实现原型模式的,但是不管用哪种方式,深拷贝都比浅拷贝花时间和空间,所以还是酌情考虑。其实在现在已经有很多针对浅拷贝和深拷贝的工具类

  • 深拷贝(deep copy):SerializationUtils
  • 浅拷贝(shallow copy):BeanUtils

6、应用场景

  • 当一个系统应该独立于它的产品创建,构成和表示时。

  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载。

  • 为了避免创建一个与产品类层次平行的工厂类层次时。

  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

代码举例:需要创建 10 个 StudentVO 对象,依次调用一个创建好的 StudentVO 对象的 clone 方法 10 次,即可创建 10 个不同的对象。(这是频繁创建大量的对象 , 该场景下适合使用原型模式)

public class Main {
    public static void main(String[] args) {
        try {
            // 测试使用 clone 方法实现的原型模式 , 使用原型模式创建 10 个对象
            StudentVO prototypeStuVO = new StudentVO();
            for (int i = 0; i < 10; i++) {
                // 1 . 使用 clone 方法创建对象
                StudentVO student = (StudentVO) prototypeStuVO.clone();
                // 2 . 设置克隆出的对象参数
                student.setName("Tom" + i);
                student.setAge(10 + i);
                System.out.println(student);
            }
        } catch (CloneNotSupportedException e) {
            //捕获 clone 方法可能产生的异常
            e.printStackTrace();
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PointNN:一种无参数化的高效点云特征提取方法

前言 本文需要一些三维点云相关基础&#xff0c;非常适合深蓝学院修过相关课程的同学阅读。 点云处理从最早期的手工设计特征&#xff0c;到之后渐渐有一些深度学习的尝试&#xff0c;经历了 multi-view或者3D卷积等等的混沌时期&#xff0c;知道 pointnet 的横空出世&#x…

微服务治理框架(Istio)的认证服务与访问控制

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/130152887 一、认证服务 1.1、基于JWT的认证 在微服务架构下&#xff0c;每个服务是无状态的&#xff0c;由于服务端需要存储客户端的登录状态&#xff0c;因此传统的session认证方式在微服务中不再适…

分享:前端开发使用基于 ChatGPT 的各类 AI Copilot 辅助开发

前言 现在谁还没听过 ChatGPT&#xff0c;通没通网我不确定&#xff0c;但一定不是搞开发的 网上各种教注册OpenAI账号的、卖key的&#xff0c;然后就可以去各类基于ChatGPT api的插件、应用使用。但是这类都属于不合规的方式&#xff0c;这里不推荐 虽然因为种种原因&#x…

nuxt.js - 【最新】简单快捷的 element ui 组件库的主题色更改,批量统一覆盖替换解决方法,无需npm装包。(适用于新手小白的方法,很简单)

效果图 最新解决方案,简单便捷且不用npm安装任何第三方包就能搞定。 原来的主题色是蓝色 ,可以通过本篇博客提供的方法,统一变成其他主题颜色,比如下面的紫色: 下面就是真实的运行效果,保证可行~ 这样就不用每个组件单独去写样式覆盖颜色了! 定制主

【C++】STL理解【容器】

【C】STL理解【容器】 1. STL概念引入 长久以来&#xff0c;软件界一直希望建立一种可重复利用的东西&#xff0c;以及一种得以制造出”可重复运用的东西”的方法&#xff0c;从函数(functions)&#xff0c;类别(classes),函数库(function libraries),类别库(class libraries…

Python中的type与isinstance的区别

Python中的type()函数和isinstance()函数是两个常用的类型判断函数&#xff0c;它们可以用来判断变量的类型&#xff0c;接下来让我们一起来看一下它们的用法。type()函数type()函数用于获取一个变量的类型&#xff0c;它的语法是&#xff1a;type(变量)。调之后会返回变变量的…

关于修改压缩包内的文件(Excel...)内容的解决方法

关于修改压缩包内的文件&#xff08;Excel…&#xff09;内容的解决方法 前提&#xff1a; &#x1f4a9; 如果能在压缩前就修改完成就不需要修改压缩包内的文件 &#x1f4a9; 如果能在压缩前就修改完成就不需要修改压缩包内的文件 &#x1f4a9; 如果能在压缩前就修改完成…

通讯框架与Netty

一、网络通讯基础 1、OSI七层模型 2、一个域名底层是如何解析 浏览器访问域名&#xff0c;根据域名先从本地host文件C:\Windows\System32\drivers\etc\hosts文件 查找匹配对应的ip与域名&#xff0c;如果本地Host文件 没有的情况下&#xff0c;则联网去电信运营商查找。 3、什…

拉曼光谱的airPLS处理算法原理及MATLAB示例

一、拉曼光谱及airPLS算法 拉曼光谱被称作物质的“指纹谱”&#xff0c;能够表征分子的特征官能团&#xff0c;具有极高的特异性&#xff0c;在检测传感领域有极大的应用前景。但拉曼散射强度低&#xff0c;在实际的检测应用过程中还会受到噪声的影响。 为减少拉曼光谱中的…

程序员的“灵魂笔记本“:五款高效笔记软件推荐

大家好&#xff0c;我是 jonssonyan。作为一名程序员&#xff0c;我们经常需要记录和整理大量的代码、知识和项目信息&#xff0c;以便在日后能够高效地进行查阅和复用。而好用的笔记软件则成为了我们的"灵魂笔记本"&#xff0c;帮助我们提高工作效率。在这篇文章中&…

十一、存储技术基础

&#xff08;一&#xff09;独立磁盘冗余阵列RAID RAID级别 特点 需要磁盘 磁盘利用率 容错性 冗余性 热备盘选项 典型应用 RAID0 条带 2 全部 无 无 无 无故障的迅速读写&#xff0c;要求安全性不高&#xff0c;如图形工作站等 RAID1 镜像 2 50% 有 复制…

代码随想录_二叉树_leetcode112、113

leetcode112 路径总和 112. 路径总和 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返…

【血泪建议】软件测试岗位现状,可惜之前没人告诉我,肠子都晦青了....

谈到现状&#xff0c;国内的软件测试行情目前呈现了两极分化的极端情况。 一个是早期的手工测试人员吐槽工作不好做&#xff0c;即使有工作也是外包&#xff0c;而且薪资太低&#xff1b;一方面是很多互联网企业感叹自动化测试人才难找&#xff0c;有技术的自动化测试工程师&a…

Linux中执行命令

目录 一、命令格式 二、查看命令帮助 三、date命令 四、timedatectl命令 五、查看目录下的文件&#xff1a;ls&#xff08;list&#xff09; 一、命令格式 命令格式&#xff1a;主命令 选项 参数&#xff08;操作对象&#xff09; 命令分为两类&#xff1a; 内置命…

要刹车?生成式AI迎新规、行业连发ChatGPT“警报”、多国考虑严监管

4月13日消息&#xff0c;据中国移动通信联合会元宇宙产业工作委员会网站&#xff0c;中国移动通信联合会元宇宙产业工作委员会、中国通信工业协会区块链专业委员会等&#xff0c;共同发布“关于元宇宙生成式人工智能&#xff08;类 ChatGPT&#xff09;应用的行业提示”。提示内…

wmv格式的视频怎么转成mp4,4种方法简单易学

你知道wmv格式的视频怎么转成mp4吗&#xff1f;wmv和mp4都是视频文件格式&#xff0c;wmv格式是由微软开发的一种数字容器格式&#xff0c;它主要适用于电脑客户端。但由于其兼容性不佳&#xff0c;可能导致无法播放或出现错误。相比之下&#xff0c;mp4格式具有更广泛的兼容性…

Maven项目的JDK版本不一致引发的问题

1.运行提示java: 错误: 不支持发行版本 5&#xff08;改成JDK8&#xff09; 2.运行提示java&#xff1a;-source 8 中不支持 instanceof 中的模式匹配(改成JDK17) 解决方案&#xff08;以JDK8为例&#xff09; 1.普通Maven项目 通过指定Maven插件的JDK版本解决 <build>…

使用CentOS8实现Nginx负载均衡反向代理与安装MySQL数据库(涉及CentOS8、Nginx、MySQL、Flask)

文章目录1. 安装并配置CentOS8虚拟机1.1 安装1.2 设置静态IP(看需求不一定要设置)1.3 开启SSH\开启关闭端口\关闭开启防火墙1.4 虚拟机与本机互相复制粘贴、传输文件2. 如何在CentOS 8安装Python3. 解决错误&#xff1a;为 repo appstream 下载元数据失败4. 安装Nginx4.1 [关于…

Shader Graph8-输入Vector

一、三个向量 Vector叫做矢量或者向量&#xff0c;向量更偏向于数学&#xff0c;矢量更偏向于图形。下面三种Vector我们用的最多&#xff0c;红色叫Camera Vector相机向量、蓝色叫Surface Normal表面法线、黄色叫Light Vector光向量。 每个面都有法线&#xff0c;法线向量是这…

七大排序算法

文章目录1. 冒泡排序2. 插入排序3. 希尔排序4. 选择排序5. 堆排序6. 快速排序7. 归并排序1. 冒泡排序 从 0 号下标开始遍历&#xff0c;相邻两个数相互比较&#xff0c;如果左边的数大于右边的数&#xff0c;执行交换操作&#xff0c;最终每一趟冒泡都会将一个最大的数移到最右…