原型模式(创建型)

news2024/11/24 9:40:21

一、前言

        原型模式是一种创建型设计模式,它允许在运行时通过克隆现有对象来创建新对象,而不是通过常规的构造函数创建。在原型模式中,一个原型对象可以克隆自身来创建新的对象,这个过程可以通过深度克隆或浅克隆来实现。简单说原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

        原型模式包含三个角色:

        ①Prototype(抽象原型类):定义用于克隆自身的接口,通常是一个抽象类或接口,其中声明了一个克隆方法 clone(),用于创建一个新的对象,具体的克隆操作由子类实现。

        ②ConcretePrototype(具体原型类):实现 Prototype 接口,实现 clone() 方法,完成对象的克隆操作。每个具体原型类都有自己的一些属性和方法,不同的具体原型类之间具有不同的实现方式。

        ③Client(客户类):使用原型模式创建新的对象,在原型模式中,客户端通过克隆接口来创建新的对象,而不是通过实例化的方式。客户端需要获取一个原型对象,并通过克隆方法来创建新的对象,从而避免了创建新对象的开销。

        原型模式的克隆分为浅拷贝和深拷贝两种。

二、浅拷贝

        创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

        这里抽象原型类不用自己创建,都是Object,里面有clone()方法:

        具体原型类实现Cloneable,里面重写clone方法,直接super调用父类的clone方法即可:

import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 10:19
 * @description
 */
public class UserPrototype implements Cloneable{

    private String name;

    private Integer age;

    private List<String> messages;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public List<String> getMessages() {
        return messages;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

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

        客户类:

import java.util.ArrayList;
import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 10:22
 * @description
 */
public class PrototypeClient {
    public static void main(String[] args) throws CloneNotSupportedException {
        UserPrototype userPrototype1 = new UserPrototype();

        userPrototype1.setName("tom");
        userPrototype1.setAge(18);
        List<String> messages1 = new ArrayList<>();
        messages1.add("test");
        userPrototype1.setMessages(messages1);

        UserPrototype userPrototype2 = userPrototype1.clone();

        System.err.println(String.format("两个对象是否一致:%s", userPrototype1 == userPrototype2));
        System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototype1.getName() == userPrototype2.getName()));

        System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototype1.getMessages() == userPrototype2.getMessages()));

        System.err.println(userPrototype1.getMessages());
        System.err.println(userPrototype2.getMessages());

        messages1.clear();
        messages1.add("test1");
        System.err.println(userPrototype1.getMessages());
        System.err.println(userPrototype2.getMessages());


    }
}

        运行结果:

        通过结果可以看到,通过克隆的对象与原型对象的引用不一致,说明再内存中存在两个不同的对象,一个是原型对象,一个是克隆生成的对象。而这里属性都显示是一致的,结果都是true,说明两个对象的成员对象是同一个,也就是对象本身是复制了,但是其成员的对象再内存中没有复制。并且我们修改原型的messages属性,克隆对象的messages也跟着改变,说明属性确实是同一个对象的引用。

三、深拷贝

        深拷贝中除了原型对象与克隆生成的对象被复制以外,里面的属性也需要被复制,即地址都不一样。这里深拷贝实现了Serializable,即进行了序列化。

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

/**
 * @Author dengyifan
 * @create 2023/11/10 15:43
 * @description
 */
public class UserPrototypeDeep implements Serializable {
    private String name;

    private Integer age;

    private List<String> messages;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public List<String> getMessages() {
        return messages;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

    protected UserPrototypeDeep deepClone() throws Exception{
        ByteArrayOutputStream bao=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bao);
        oos.writeObject(this);
        //将对象从流中取出
        ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(bis);
        return (UserPrototypeDeep) ois.readObject();
    }
}
import java.util.ArrayList;
import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 15:46
 * @description
 */
public class PrototypeDeepClient {
    public static void main(String[] args) throws Exception {
        UserPrototypeDeep userPrototypeDeep1 = new UserPrototypeDeep();
        userPrototypeDeep1.setAge(19);
        userPrototypeDeep1.setName("tom");
        List<String> messages1 = new ArrayList<>();
        messages1.add("test");
        userPrototypeDeep1.setMessages(messages1);

        UserPrototypeDeep userPrototypeDeep2 = userPrototypeDeep1.deepClone();

        System.err.println(String.format("两个对象是否一致:%s", userPrototypeDeep1 == userPrototypeDeep2));
        System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototypeDeep1.getName() == userPrototypeDeep2.getName()));

        System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototypeDeep1.getMessages() == userPrototypeDeep2.getMessages()));

        System.err.println(userPrototypeDeep1.getMessages());
        System.err.println(userPrototypeDeep2.getMessages());
        messages1.clear();
        messages1.add("test1");
        System.err.println(userPrototypeDeep1.getMessages());
        System.err.println(userPrototypeDeep2.getMessages());

    }
}

        运行结果:

        可以看出除了对象本身不一致以外,里面的属性也不一致,当修改原型对象里面引用对象的属性时,克隆对象的属性不会发生变化,即也证明属性不是同一个对象。

        对于原型模式的应用场景,主要有以下几点:

1. 当对象的创建过程非常复杂或耗费大量资源时,可以使用原型模式来复制一个现有对象,从而避免重复创建对象。

2. 当需要创建多个相似的对象时,可以使用原型模式来减少重复代码,提高代码的重用性。

3. 当创建对象的过程需要大量的数据准备或配置时,可以使用原型模式来复制一个已经配置好的对象,从而避免重复的数据准备或配置过程。

4. 当需要动态生成对象的子类时,可以使用原型模式来复制一个已有的对象,并对其进行修改,从而避免重新编写子类的代码。

5. 当需要保护对象的状态时,可以使用原型模式来创建对象的副本,并将副本交给其他对象使用,从而避免原始对象的状态被修改。

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

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

相关文章

【Java SE】类和对象(上)

目录 一. 面向对象的初步认知 1.1 什么是面向对象 1.2 面向对象与面向过程 二. 类定义和使用 2.1 简单认识类 2.2 类的定义格式 三. 类的实例化 3.1 什么是实例化 3.2 实例化对象 四. this引用(重点&#xff09; 4.1 为什么要有this引用 4.2 this的使用 4.3 this引…

lua环境安装

文章目录 Linux 系统上安装Mac OS X 系统上安装Window 系统上安装 Lua第一个 Lua 程序 Linux 系统上安装 Linux & Mac上安装 Lua 安装非常简单&#xff0c;只需要下载源码包并在终端解压编译即可&#xff0c;本文使用了5.3.0版本进行安装&#xff1a; curl -R -O http://…

openMMLab的mmcv和mmdet、mmdet3d、mmseg版本对应关系

openmmlab提供了MIM来统一安装其多个mm功能框架包https://github.com/open-mmlab/mim&#xff0c;但是需要不借助MIM安装时&#xff0c;这里怎么确定要安装什么版本的mmcv和mmdet、mmdet3d、mmseg&#xff0c;在openmmlab网站主页上没有一个容易能找到的完整表格页面来详细记录…

Dell戴尔灵越Inspiron 7700 AIO一体机电脑原厂预装Windows10系统

链接&#xff1a;https://pan.baidu.com/s/1-slgR9t4Df_eko0Y6xaeyw?pwdmk0p 提取码&#xff1a;mk0p 灵越7700一体机原装出厂系统自带声卡驱动、无线网卡驱动、面部识别等所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、MyDell等预装程序 由于时间关系,…

GF0-57CQD-002 测量参数:加速度、速度、位移–现场可配置

GF0-57CQD-002 测量参数:加速度、速度、位移–现场可配置 GF0-57CQD-002 是一款创新的双通道变送器&#xff0c;专为精确的振动测量而设计。它激励并读取来自加速度计的信号&#xff0c;并将整体振动值作为电流/电压信号传输。它测量加速度、速度和位移等不同参数的振动。配置…

HTTP和HTTPS详解

一)什么是HTTP协议 1)HTTP协议是倾向于相遇业务层次上面的一种协议&#xff0c;传输层协议主要考虑的是端对端之间的一个传输过程&#xff0c;TCP重点进行关注的是可靠传输&#xff1b;咱们的HTTP/1&#xff0c;HTTP/2是基于TCP的&#xff0c;但是咱们的HTTP/3是基于UDP的&…

【优选算法系列】【专题五位运算】第二节.371. 两整数之和and137. 只出现一次的数字 II

文章目录 前言一、两整数之和 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写二、只出现一次的数字 II 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写总结 前言 一、两整数之和 …

故障诊断模型 | Maltab实现RF随机森林的故障诊断

效果一览 文章概述 故障诊断模型 | Maltab实现RF随机森林的故障诊断 模型描述 RF善于处理高维数据,特征遗失数据,和不平衡数据 (1)训练可以并行化,速度快 (2)对高维数据集的处理能力强,它可以处理成千上万的输入变量,并确定最重要的变量,因此被认为是一个不错的降…

【overleaf参考文献引用】Citation `r51‘ on page 1 undefined on input line 46

overleaf 编辑插入参考文献出现如下问题&#xff1a; 显示如下&#xff1a;连着三个参考文献有一个显示为问号&#xff0c;latex的错误如上&#xff1a; Citation r51 on page 1 undefined on input line 46 问题原因&#xff1a; 在文档的第一页&#xff08;Page 1&#xff0…

ElasticSearch7.x - HTTP 操作 - 查询文档操作

查询索引下的所有文档 http://192.168.254.101:9200/shopping/_search 条件查询 请求路径上添加条件:http://192.168.254.101:9200/shopping/_search?q=category:小米 请求体上添加条件:http://192.168.254.101:9200/shopping/_search 请求体内容 {"query" :{&qu…

Linux 部署Sentinel控制台

Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 1.版本选择 SpringCloudAlibaba SpringClo…

uniapp+uviewPlus+vue3+ts+pinia+vite+echarts 开发基础模板,开箱即用,非常顺手

github仓库地址&#xff1a;https://github.com/Sjj1024/uniapp-vue3 使用 uniapp vue3 ts pinia vite echarts 开发基础模板&#xff0c;拿来即可使用&#xff0c;不要删除 yarn.lock 文件&#xff0c;否则会启动报错&#xff0c;这个可能和 pinia 的版本有关&#xff0…

k8s存储

nfs 理论上nfs 其实并不是存储设备&#xff0c;它是一种远程共享存储服务。 k8s 存储卷 volume emptyDir&#xff1a;可以实现pod中的容器之间共享数据&#xff0c; 但是存储卷不能持久化数据&#xff0c;且会随着pod的生命周期一起删除。 hostpash&#xff1a;可以实现持久…

iOS应用加固方案解析:ipa加固安全技术全面评测

​ 在移动应用开发领域&#xff0c;iOS应用的安全性一直备受关注。ipaguard作为一款专业的iOS应用加固方案&#xff0c;采用混淆加密技术&#xff0c;旨在保护应用免受破解、逆向和篡改等风险。本文将深入探讨ipaguard的产品功能、安全技术及其在iOS应用加固领域中的核心优势和…

Electron-vue出现GET http://localhost:9080/__webpack_hmr net::ERR_ABORTED解决方案

GET http://localhost:9080/__webpack_hmr net::ERR_ABORTED解决方案 使用版本解决方案解决总结 使用版本 以下是我解决此问题时使用的electron和vue等的一些版本信息 【附】经过测试 electron 的版本为 13.1.4 时也能解决 解决方案 将项目下的 .electron-vue/dev-runner.js…

【Copilot】登录报错 Extension activation failed: “No auth flow succeeded.“(VSCode)

问题描述 Visual Studio Code 登录 GitHub Copilot 插件报错。 在浏览器中成功授权 GitHub 账户&#xff0c;返回 VSCode 后仍然报错。 [ERROR] [default] [2023-11-06T12:34:56.185Z] Extension activation failed: "No auth flow succeeded."原因分析 网络环境问…

3D物理模拟和视觉特效软件SideFX Houdini mac中文介绍

SideFX Houdini for mac是一款3D物理模拟和视觉特效软件&#xff0c;几乎所有好莱坞特效电影里的物理模拟&#xff0c;包括碎裂&#xff0c;烟尘&#xff0c;碰撞&#xff0c;火焰&#xff0c;流体等模拟&#xff0c;都看得到它的身影。其独特的节点式操作方式&#xff0c;尤其…

[工业自动化-9]:西门子S7-15xxx编程 - PLC主站 - 信号量:模拟量

目录 前言&#xff1a; 一、模拟量模块 1.1 概述 1.2 安装 1.3 模拟量链接线 二、模拟量常见问题 2.1 两线制、四线制&#xff08;电流&#xff09; 2.2 模拟量模块的参数 2.3 差分信号与单端信号 三、如何防止电磁干扰 3.1 概述 3.2 工业现场的电磁干扰源来源 3.…

uni-app基于vite和vue3创建并集成pinia实现数据持久化

一、uni-app基于Vite和Vue3创建并集成pinia实现数据持久化 文章目录 一、uni-app基于Vite和Vue3创建并集成pinia实现数据持久化1.如何创建基于Vite和Vue3的uni-app项目&#xff1f;2.选择其中一个分支&#xff0c;就是一个脚手架 二、以下都是基于vite-ts版本创建和配置1.目录结…

csharp写一个招聘信息采集的程序

csharp爬虫是一种用于自动化抓取网页内容的程序。它可以通过模拟人类浏览器的行为&#xff0c;自动访问网站并抓取所需的数据。csharp爬虫可以用于各种场景&#xff0c;例如数据挖掘、搜索引擎优化、竞争情报等。但是&#xff0c;使用csharp爬虫需要注意一些问题&#xff0c;例…