设计模式之创建型模式---原型模式(ProtoType)

文章目录

  • 概述
  • 类图
  • 原型模式优缺点
    • 优点
    • 缺点
  • 代码实现

在这里插入图片描述

概述

在有些系统中,往往会存在大量相同或者是相似的对象,比如一个围棋或者象棋程序中的旗子,这些旗子外形都差不多,只是演示或者是上面刻的内容不一样,若此时使用传统的构造函数创建对象,那么简单的对象还好,遇到稍微复杂一点的对象就会耗时耗资源,而使用原型设计模式会让对象的生成高效很多

类图

在本文中使用了人和身体的例子来演示原型模式,假如咱要造一个人的对象,需要设置人的属性,姓名,年龄等,然后给他设置身体,大脑等器官,本例只是为了展示原型模式,只简单的做了Person和Body的结合。
原型模式主要有三个角色
1.抽象原型类:它定义了具体的原型对象必须实现的接口。如本例子中的IProtoType接口
2.具体原型类: 实现抽象原型接口中的复制对象的方法,比如本例中的实现了原型接口中的 clone(),deepClone()方法的Person类
3.访问类: 使用具体原型类中的复制对象方法生成新的对象,比如本例中中的Client

结合本文中的例子,原型设计模式的类图如下所示:

在这里插入图片描述

原型模式优缺点

优点

原型设计模式的优点主要有两点,如下所示:
(1)可以优化性能:在JAVA语言中,可以通过实现Cloneable接口,重写clone方法来达到复制对象的目的,这种方式是基于内存二进制流的复制,在性能上比直接使用new关键字创建一个对象高很多。

(2)可以使用原型模式中的深克隆方式保存对象的状态:我们可以使用原型模式将对象复制一份,并将其状态保存起来。简化了创建对象的过程,在需要的时候直接使用我们保存的对象,例如遇到需要恢复到历史某一状态的需求时,或者是需要实现撤销操作的需求时,都可以考虑原型模式

缺点

当然,万事万物有优点就会有缺点,原型模式的缺点主要有三个,如下所示:
(1)需要为每个类都配置一个克隆方法
(2)clone方法位于类的内部,当对已有的类进行修改的时候,需要修改对应的实现代码。不符合开闭原则(对修改关闭,对扩展开放)
(3)当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多层嵌套引用时,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦

深克隆:不仅拷贝对象的本身,而且拷贝对象包含的引用指向的所有对象
浅克隆:仅拷贝对象的本身,而不拷贝对象包含的引用指向的对象
使用一个例子解释深克隆和浅克隆

public class Person {
    private static final Long VERSION = 1000L;
    private String name;
    private int age;
    private Body body;
// 省略构造函数以及get,set方法
}

比如我们要拷贝上面的Person对象,如果使用深克隆的方式拷贝,这时候Person对象中包含的Body对象也会被拷贝,也就是说,深克隆拷贝出的对象和原来的对象是完全独立的,我们修改新克隆出的对象,不会影响原来的Person对象。假如使用浅克隆,这时只会拷贝Person中包含的Body对象的引用,也就是说使用浅克隆拷贝出来的新对象中包含的Body对象和原来的对象中包含的一样,因为浅克隆将Body的引用拷贝给了新克隆出的对象,这时候如果修改新克隆出的对象,那么原来的Person对象的Body也会跟着变,后面会有例子证实这点

代码实现

本文中,我们使用人和身体的例子来演示原型设计模式。首先我们定义出原型模式的接口,如下所示:

public interface IPrototype extends Cloneable{
    Person deepClone() throws IOException, ClassNotFoundException;
}

原型模式的接口继承自Java的Cloneable接口,其中包含了一个clone()方法,用于实现浅克隆,而我们定义的接口中
包含了一个deepClone()方法,用于实现深克隆。

然后就是定义一个类实现原型设计模式的接口:

public class Person implements IPrototype, Serializable {
    private static final Long VERSION = 1000L;
    private String name;
    private int age;

    private Body body;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setBody(Body body) {
        this.body = body;
    }

    public Body getBody() {
        return body;
    }
    
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    public Person deepClone() throws IOException, ClassNotFoundException {
        // 将对象写入到流中
        ByteArrayOutputStream byteArrayOutputStream =
                new ByteArrayOutputStream();
        ObjectOutputStream outputStream =
                new ObjectOutputStream(byteArrayOutputStream);
        outputStream.writeObject(this);

        // 将对象从流中取出
        ByteArrayInputStream byteArrayInputStream =
                new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

        ObjectInputStream inputStream =
                new ObjectInputStream(byteArrayInputStream);
        return (Person) inputStream.readObject();
    }

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

需要注意的是,为了实现深克隆,我们需要借助Java的Serializable 接口标识本类可以被序列化。Person类中包含了基本类型的成员变量以及引用类型的成员变量Body,Body的定义如下:

public class Body implements Serializable {
    private static final Long VERSION = 1001L;
    private String sex;
    private String hand;

    public Body(String sex, String hand) {
        this.sex = sex;
        this.hand = hand;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getHand() {
        return hand;
    }

    public void setHand(String hand) {
        this.hand = hand;
    }

    @Override
    public String toString() {
        return "Body{" +
                "sex='" + sex + '\'' +
                ", hand='" + hand + '\'' +
                '}';
    }
}

为了能实现序列化,Body类也要实现Serializable接口。当需要使用浅克隆的时候,我们就通过Person对象的clone()方法来生成,,当需要使用深克隆的时候,我们就使用deepClone()方法来生成。

最后就是使用对应的克隆方法生成克隆对象

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 创建一个Person对象
        Person person = new Person("walt", 18);
        // 创建出Body对象
        Body body = new Body("男", "男生的手");
        person.setBody(body);
        System.out.println("原始的Person: " + person);

        // 使用浅克隆生成一个克隆对象
        Person clonePerson = person.clone();
        System.out.println("克隆的Person: " + clonePerson);


        // 获取到克隆对象的Body并做修改
        Body cloneBody = clonePerson.getBody();
        cloneBody.setSex("女");
        cloneBody.setHand("女生的手");
        clonePerson.setBody(cloneBody);

        // 分别打印出克隆的对象和原始对象
        System.out.println("克隆Person: " + clonePerson + " ,原始Person: " + person);
        // 检查克隆对象的body和原始对象的body是否是同一个,是就为浅克隆,不是为深克隆
        System.out.println("克隆的Body是否等于原始的Body: " + (body == cloneBody));

        // 使用深克隆生成一个对象
        Person deepClonePerson = person.deepClone();
        // 获取到深克隆后的person对象的body并修改
        Body deepCloneBody = deepClonePerson.getBody();
        deepCloneBody.setSex("深克隆男孩子");
        deepCloneBody.setHand("深克隆男生的手手");
        deepClonePerson.setBody(deepCloneBody);

        // 打印出深克隆后的对象和原始的对象
        System.out.println("深克隆Person: " + deepClonePerson + " ,原始Person: " + person);
        // 检查克隆对象的body和原始对象的body是否是同一个,是就为浅克隆,不是为深克隆
        System.out.println("深克隆的Body是否等于原始的Body: " + (body == deepCloneBody));
    }
}

运行结果:
在这里插入图片描述

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

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

相关文章

酷开科技以内容为契机,酷开系统向消费者需求的深度挖掘迈进一步

酷开系统还拥有强大的内容资源和推荐算法,能够根据消费者的兴趣爱好为其提供个性化的推荐服务。无论是电影、电视剧、综艺节目,还是新闻、体育、娱乐资讯,酷开系统都能帮助大家快速找到感兴趣的内容,并且通过智能推荐算法不断优化…

Java | Leetcode Java题解之第112题路径总和

题目: 题解: class Solution {public boolean hasPathSum(TreeNode root, int sum) {if (root null) {return false;}if (root.left null && root.right null) {return sum root.val;}return hasPathSum(root.left, sum - root.val) || has…

Leetcode 环形链表|| 快慢指针解法

但是我们不知道 aaa 的值,该怎么办?依然是使用双指针法。考虑构建一个指针,此指针需要有以下性质:此指针和 slow 一起向前走 a 步后,两者在入口节点重合。那么从哪里走到入口节点需要 aaa 步?答案是链表头节…

使用 Azure DevOps 和 Azure Web Apps 进行 .NET Core 应用的 CI/CD

概览 在现代软件开发中,快速部署和高效的版本控制系统是非常关键的。通过利用 Azure DevOps 和 Azure Web Apps,开发团队可以实现自动化的持续集成和持续部署(CI/CD),从而加快从开发到生产的过程。接下来我们一步步来…

python写接口性能测试

import time import requestsdef measure_response_time(api_url):try:start_time time.time()response requests.get(api_url, timeout10) # 设置超时时间为10秒end_time time.time()response_time end_time - start_timeprint(f"接口 {api_url} 的响应时间为&#…

【手把手带你搓组件库】从零开始实现Element Plus

从零开始实现Element Plus 前言亮点项目搭建1、创建项目初始化monorepo创建 .gitignore目录结构安装基础依赖配置文件创建各个分包入口utilscomponentscoreplaytheme 2、创建VitePress文档3、部署到Github Actions生成 GH_TOKENGitHub Page 演示 4、总结 前言 在本文中&#xf…

SVM原问题与对偶问题

目的:求出我们的f(X),它代表着我们X映射到多维的情况,能够帮我们在多维中招到超平面进行分类。 1.优化问题: 1.1推荐好书: 1.2 优化理论中的原问题: 原问题和限制条件如下: 这是一个泛化性…

无人机飞手:ASFC无人机和航模爱好者证书详解

ASFC无人机和航模爱好者证书是由中国航空运动协会(ASFC)颁发的一种无人机操作资格认证。这种证书在无人机和航模爱好者群体中享有广泛的认可度,并被视为操作无人机的一种重要资质。 ASFC证书的定义和用途十分明确。它是民航局颁发的民用无人驾…

《最新出炉》系列入门篇-Python+Playwright自动化测试-40-录制生成脚本

宏哥微信粉丝群:https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 各种自动化框架都会有脚本录制功能, playwright这么牛叉当然也不例外。很早之前的selenium、Jmeter工具,发展到每种浏览器都有对应的录制插件。今天我们…

基于 vuestic-ui 实战教程

1. 前言简介 Vuestic UI是一个基于开源Vue 3的UI框架。它是一个MIT许可的UI框架,提供了易于配置的现成前端组件,并加快了响应式和快速加载Web界面的开发。它最初于2021年5月由EpicMax发布,这就是今天的Vuestic UI。 官网地址请点击访问 体验…

【DASBOOK】Mark loves cat

文章目录 一、工具下载二、Mark loves cat解题感悟 一、工具下载 克隆dirsearch仓库: git clone https://github.com/maurosoria/dirsearch.git下载 githack工具 git clone https://github.com/lijiejie/GitHack.git二、Mark loves cat 用dirsearch扫描目录&…

urllib_post请求_百度翻译之详细翻译

百度翻译有一个详细翻译的接口: post请求: 请求参数(较多): 打印之后,发现有问题: 改一下请求头: 将Accept-Encoding注释掉,因为我们使用的是utf-8编码: 加上…

STM32F1之OV7725摄像头·像素数据输出时序、FIFO 读写时序以及摄像头的驱动原理详解

STM32F1之OV7725摄像头-CSDN博客 STM32F1之I2C通信-CSDN博客 目录 1. 像素数据输出时序 2. FIFO 读写时序 2.1 写时序 2.2 读时序 3. 摄像头的驱动原理 1. 像素数据输出时序 主控器控制 OV7725 时采用 SCCB 协议读写其寄存器,而它输出图像时则使用 VGA 或…

无线技术整合到主动噪声控制(ANC)增强噪声降低性能

主动噪声控制(ANC)已成为一种广泛使用的降噪技术。基本原理是通过产生与外界噪音相等的反向声波,将噪音中和,从而达到降噪的效果。ANC系统通常包括以下几个部分:参考麦克风、处理芯片、扬声器和误差麦克风。参考麦克风…

设计循环队列(C语言)怎会如此简单!!!

目录 题目题目分析 解答结构体初始化判空判满插入删除去队头数据取队尾数据队列的销毁 题目 链接: 题目 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它…

对于高速信号完整性,一块聊聊啊(12)

常见的无源电子器件 电子系统中的无源器件可以按照所担当的电路功能分为电路类器件、连接类器件。 A、电路类器件: (1)二极管(diode) (2)电阻器(resistor) &#xf…

CAD二次开发(4)-编辑图形

工具类:EditEntityTool.cs using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Th…

✨✨使用jq+layui-tab+echarts+swiper实现选项卡轮播联动图表展示功能✨✨

使用jqlayui-tabechartsswiper实现选项卡轮播联动图表展示功能 ✨一、实现功能 🌟技术框架 使用layui-tab实现tabs切换使用swiper.js实现轮播效果使用echarts.js实现图表展示 🌟功能详情 布局为上中下:tab选项上,内容区为中&…

大作业爬取手机数据,实现手机推荐系统以及朋友圈手机论坛

1、功能简介 (1)用户注册与用户登录 (2)手机搜索、手机比拼、手机个性化推荐 (3)点击搜索的手机图片会就用户行为,轮播展示用户行为,推荐点击次数靠前的手机 (4&#xf…

网络域名是什么意思

网络域名,顾名思义,就是网络上的名字,类似于现实中的地址或姓名一样,用来标识网络上的一个或一组计算机或服务器的位置,以及它们的相应服务资源。网络域名是互联网上最基础的基础设施之一,是网络通信的“标…