设计模式之原型模式

news2024/12/25 12:49:23

文章目录

  • 1.前言
    • 概念
    • 使用场景
  • 2.原型模式核心组成
    • UML图
  • 3.浅拷贝与深拷贝
    • 基本类型与引用类型
    • 浅拷贝
      • 代码演示
    • 深拷贝
      • 代码演示
  • 4.原型模式的优点与缺点

1.前言

概念

原型模式(Prototype Pattern)是用于创建重复的对象同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工作原理是将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。

使用原型模式非常简单,实现一个接口,重写一个方法即完成了原型模式。

使用场景

  • 创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得
  • 如果系统要保存对象的状态,做备份使用

2.原型模式核心组成

  • Prototype: 声明克隆方法的接口,是所有具体原型类的公共父类,Cloneable接口
  • ConcretePrototype : 具体原型类
  • Client: 让一个原型对象克隆自身从而创建一个新的对象

UML图

请添加图片描述

因为克隆比较常用,所以Java提供了Cloneable接口,所以我们并不需要去编写Prototype接口,只需要实现Cloneable接口即可。不过Cloneable实现的是浅拷贝,如果想实现深拷贝,我们可以使用Serializable 接口。下面我们会细说分别使用浅拷贝与深拷贝实现原型模式。

3.浅拷贝与深拷贝

基本类型与引用类型

在Java中,基本类型有整数类型(long、int、short、byte),浮点类型(float、double),字符类型(char),布尔类型(boolean)。

除此之外所有的类型都是引用类型

从存储来说,基本类型与引用类型的区别在于:

  • 在方法中定义的非全局基本数据类型变量的具体内容是存储在栈中的
  • 引用数据类型变量,栈中存放的是其具体内容所在内存的地址,而其具体内容都是存放在堆中的

浅拷贝

在浅拷贝中:

  • 如果原型对象的成员变量是基本数据类型,将复制一份给克隆对象;
  • 如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象, 也就是说原型对象和克隆对象的成员变量指向相同的内存地址

代码演示

接下来我们使用Resume类来讲解。

具体原型类

package ProtoType.ShallowCopy;

import java.util.List;

/**
 * @Author: 少不入川
 * @Date: 2022/11/28 21:19
 */
public class Resume implements Cloneable{
    private String name;
    private int age;
    private List<String> technologyStack;

    public Resume(){}

    public Resume(String name, int age, List<String> technologyStack){
        this.name = name;
        this.age = age;
        this.technologyStack = technologyStack;
    }

    public List<String> getTechnologyStack() {
        return technologyStack;
    }

    public void setTechnologyStack(List<String> technologyStack) {
        this.technologyStack = technologyStack;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", technologyStack=" + technologyStack +
                '}';
    }

    // 使用public修饰符方便调用
    @Override
    public Object clone(){
        Resume resume = null;
        try{
            resume = (Resume) super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return resume;
    }
}

测试类

package ProtoType.ShallowCopy;

import java.util.ArrayList;

/**
 * @Author: 少不入川
 * @Date: 2022/11/28 21:58
 */
public class ShallowCopyTest {
    public static void main(String[] args) {
        Resume resume1 = new Resume("张三", 20, new ArrayList<>());
        Resume resume2 = (Resume) resume1.clone();

        // 引用型变量使用==判等判断的是引用型变量指向地址是否相同
        System.out.println("两个对象所指向内存地址是否相同:" + (resume1 == resume2));
        System.out.println("两个对象的成员变量name所指向的内存地址是否相同:" + (resume1.getTechnologyStack() == resume2.getTechnologyStack()));
    }
}

控制台结果

两个对象所指向内存地址是否相同:false
两个对象的成员变量name所指向的内存地址是否相同:true

深拷贝

在深拷贝中:

  • 无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象

代码演示

具体原型类

package ProtoType.DeepCopy;

import java.io.*;
import java.util.List;

/**
 * @Author: 少不入川
 * @Date: 2022/11/28 21:58
 */
public class Resume implements Serializable {
    private String name;
    private int age;
    private List<String> technologyStack;

    public Resume(){}

    public Resume(String name, int age, List<String> technologyStack){
        this.name = name;
        this.age = age;
        this.technologyStack = technologyStack;
    }

    public List<String> getTechnologyStack() {
        return technologyStack;
    }

    public void setTechnologyStack(List<String> technologyStack) {
        this.technologyStack = technologyStack;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", technologyStack=" + technologyStack +
                '}';
    }

    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);
            Resume resume = (Resume) ois.readObject();

            return resume;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }

}

测试类

package ProtoType.DeepCopy;

import java.util.ArrayList;

/**
 * @Author: 少不入川
 * @Date: 2022/11/28 22:09
 */
public class DeepCopyTest {
    public static void main(String[] args) {

        Resume resume1 = new Resume("张三", 20, new ArrayList<>());
        Resume resume2 = (Resume) resume1.deepClone();

        // 引用型变量使用==判等判断的是引用型变量指向地址是否相同
        System.out.println("两个对象所指向内存地址是否相同:" + (resume1 == resume2));
        System.out.println("两个对象的成员变量name所指向的内存地址是否相同:" + (resume1.getTechnologyStack() == resume2.getTechnologyStack()));
    }
}

控制台输出

两个对象所指向内存地址是否相同:false
两个对象的成员变量name所指向的内存地址是否相同:false

4.原型模式的优点与缺点

  • 优点
    • 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,可以提高新实例的创建效率
    • 可辅助实现撤销操作,使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用恢复到历史状态
  • 缺点
    • 需要为每一个类配备一个克隆方法,对已有的类进行改造时,需要修改源代码,违背了“开闭原则”

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

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

相关文章

Cpp知识点系列-类型转换

前言 在做题的时候发现了需要用到类型转换&#xff0c;于是在这里进行了简单的记录。 历史原因&#xff0c;慢慢整理着发现类型转换也能写老大一篇文章了。又花了时间来梳理一下就成了本文了。 cpp 之前使用的环境是DEV-C 5.4&#xff0c;而对应的GCC版本太低了。支持c11需要…

【CSS】重点知识梳理,这样上手无压力

推荐前端学习路线如下&#xff1a; HTML、CSS、JavaScript、noodJS、组件库、JQuery、前端框架&#xff08;Vue、React&#xff09;、微信小程序和uniapp、TypeScript、webpack 和 vite、Vue 和 React 码源、NextJS、React Native、后端内容。。。。。。 CSS定义&#xff1a; …

docker入门到精通一文搞定

文章目录前言一、Docker概述1.Docker为什么会出现&#xff1f;2.Docker相比VM技术3.Docker 能做什么&#xff1f;3.1 比较Docker和虚拟机技术的不同&#xff1a;3.2 DevOps (开发、运维)&#xff1a;4个特点二、Docker安装1.dokcer架构图&#xff1a;2.Docker基本组成&#xff…

python+django体质测试数据分析及可视化设计

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 B/S架构 4 本选题则旨在通过标签分类管理等方式&#xff0c;实现管理员&#xff1a;管理员&#xff1a;首页、个…

11.前端笔记-CSS盒子模型-外边距margin

1、margin 1.1 margin的语法 盒子与盒子之间的距离 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…

linux 系统的磁盘 mbr 转gpt方法

linux磁盘用fdisk格式化挂盘的格式都为mbr, 不支持大于2T的磁盘扩容&#xff0c;需要用parted转化。 查询磁盘格式 输入&#xff1a; fdisk -l 看Disk label type 的值&#xff0c;是dos 的为mbr 是gpt的为gpt 当前&#xff0c;因挂盘时&#xff0c;用的fdisk方式选gpt,挂…

基于STM32的u8g2移植以及学习

实验硬件&#xff1a;STM32F103C8T6&#xff1b;0.96寸OLED&#xff08;12864&#xff09; U8g2库开源网址&#xff1a;https://github.com/olikraus/u8g2 一、u8g2库知识 1.1 什么是u8g2&#xff1f; U8g2是嵌入式设备的单色图形库。主要应用于嵌入式设备&#xff0c;包括我…

正大国际期货:投资外盘期货如何运用K线图中十字星形态?

很多人都明白&#xff0c;做外盘期货需要学会看线图。那么K线图上面的一根两根的柱子代表的什么意思呢&#xff1f;其中星星点点的十字星又是什么意思&#xff1f;下面正大IxxxuanI详细给大家讲解一下&#xff01; 1、什么是多头十字星形态&#xff1f; 多头十字星是一种经典…

KEITHLEY 吉时利2601B源表产品技术参数

KEITHLEY 2601B 吉时利 2601B 源表让您可以比以前更快、更轻松、更经济地进行精密直流、脉冲和低频交流源测量测试。Keithley 2601B 通过结合以下特性&#xff0c;为 IV 功能测试提供竞争产品 2 到 4 倍的测试速度&#xff1a; 吉时利的高速第三代源测量单元 (SMU) 设计 嵌…

【Python】八、函数的使用

文章目录实验目的一、定义函数二、调用函数三、参数的传递和函数的返回值四、编写函数&#xff0c;输入不同的参数&#xff0c;绘制不同的科赫曲线参考代码实验截图实验目的 掌握函数的定义和调用&#xff1b;掌握函数的用法&#xff1b;理解递归&#xff1b;培养学生动手查阅资…

开源:分享4个非常经典的CMS开源项目

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️个人荣誉&#xff1a; 数据库领域优质创作者&#x1f3c6;&#x…

Spark系列之Spark安装部署

title: Spark系列 第二章 Spark安装部署 2.1 版本选择 下载地址&#xff1a; https://archive.apache.org/dist/spark 四大主要版本 Spark-0.X Spark-1.X&#xff08;主要Spark-1.3和Spark-1.6&#xff09; Spark-2.X&#xff08;最新Spark-2.4.8&#xff09; Spark-3.x&a…

降级、熔断和限流———一看就会

设定&#xff1a;A上游系统、B本系统、C下游系统 服务降级 服务降级是从整个系统B的负荷情况出发和考虑的&#xff0c;对某些负荷会比较高的情况&#xff0c;为了预防某些功能&#xff08;业务场景&#xff09;出现负荷过载或者响应慢的情况&#xff0c;在B其内部暂时舍弃对一…

【Mybatis编程:统计相册表中的数据的数量】

目录 1. 书写SQL语句 2.在AlbumMapper.java接口中添加抽象方法 3. 在AlbumMapper.xml中配置SQL语句 4. 在AlbumMapperTests.java中编写并执行测试 1. 书写SQL语句 需要执行的SQL语句大致是&#xff1a; select count(*) from pms_album 在设计抽象方法时&#xff0c;如果要…

【三维重建补充知识-0】视差、深度概念及其转换

一、基本概念 把手指放在眼前&#xff0c;分别闭上左、右眼&#xff0c;我们会发现手指与后边物体的相对位置是不同的&#xff0c;也即两眼所识别的两幅图像之间存在视觉差异&#xff0c;我们通过“视差”这一概念来表示这种差别。 该过程也可以通过两个处于同一平面的相机来模…

Ajax学习:Ajax请求基本操作

点击按钮&#xff0c;发送请求&#xff08;前端页面和服务端页面信息交流 但是不刷新页面&#xff09; 注意使用谷歌浏览器 服务器端打开&#xff1a;使用nodemon //1、导入express const expressrequire(express) //2、创建应用对象 创建web服务器 const appexpress() //3、…

软考高级系统架构师_计算机组成与结构02_高速缓存_磁盘结构_输入输出技术_总线结构_可靠性_---软考高级系统架构师005

1.Cache是Cpu与主存储器之间的速度比主存储器要块10倍左右,因为cpu用的说主存储器中的地址, 而cpu速度很快,主存储器速度慢,所以中间加了这个cache,那么这里就涉及到,怎么把cache地址转化成主存储器的地址,cpu使用地址的时候首先访问主存储器地址,但是访问的是cache,所以这个时…

第十二周学习总结 Progress Lack

关于计划的制定 FlowUs计划链接&#xff1a; 点击我进入计划 发现不足&#xff1a;首先&#xff0c;制定计划经验不足&#xff0c;制定计划应该是具体的、有任务量、完成时间。 其次任务应该是可衡量的&#xff08;比如&#xff0c;我阅读李升波老师的网站文章&#xff0c;应…

【WAX链游】发布一个免费开源的Alien Worlds【外星世界】合约脚本TLM

前言 《链游Farmers World【农民世界】爆火&#xff0c;发布一个免费开源的脚本》 在之前的文章中&#xff0c;我们分享了一个开源的农民世界(Farmers World)脚本 【OpenFarmer】&#xff1a;https://github.com/encoderlee/OpenFarmer 经过这段时间以来的不断学习&#xff…

【HTML】重点知识内容~快速上手

推荐前端学习路线如下&#xff1a; HTML、CSS、JavaScript、noodJS、组件库、JQuery、前端框架&#xff08;Vue、React&#xff09;、微信小程序和uniapp、TypeScript、webpack 和 vite、Vue 和 React 码源、NextJS、React Native、后端内容。。。。。。 HTML基本结构&#xff…