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

news2024/11/15 13:26:58

文章目录

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

在这里插入图片描述

概述

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

类图

在本文中使用了人和身体的例子来演示原型模式,假如咱要造一个人的对象,需要设置人的属性,姓名,年龄等,然后给他设置身体,大脑等器官,本例只是为了展示原型模式,只简单的做了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));
    }
}

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

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

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

相关文章

【spring】@RequestBody注解学习

RequestBody介绍 RequestBody 是 Spring Framework 中的一个注解,用于将 HTTP 请求的正文绑定到处理方法的参数上。这个注解通常用于处理 POST 或 PUT 请求,这些请求通常包含 JSON 或 XML 格式的数据。 RequestBody 将 HTTP 请求的正文内容转换成指定的…

前端vue项目遇到的问题01——那些初级问题

前端vue项目遇到的问题01——那些初级问题 1. npm install 问题1.1 依赖冲突1.1.1 详细问题1.1.2 报错原因1.1.3 解决问题1.1.3.1 方式1——无视冲突1.1.3.1 方式2——更换依赖版本 1.2 nodejs版本问题1.3 node版本正确的情况(audit问题)(这个…

OpenH264 编解码器介绍

思科 思科系统(英语:Cisco Systems, Inc.)是一间跨国际综合技术企业,总部设于加州硅谷;思科开发、制作和售卖网络硬件、软件、通信设备等高科技产品及服务,并透过子公司(例子有OpenDNS、Webex、…

【C语言】整型提升与char取值范围

整型提升介绍 C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达式中字符、短整型操作数在使用前被转换为普通整型。而这个过程是悄悄发生的。 整型提升的意义: 表达式的整型运算要在CPU…

小米财报:业绩远超预期,汽车推着手机跑!

随着一季度财报陆续出炉,企业间的分化越来越明显。 新环境下,很多公司都陷入停滞时,去讨论“掉队”已经没有多少意义,现在真正值得我们关注的,是那些在逆风情况下,还能“领先”的企业。毫无疑问&#xff0…

小程序主体变更是通过迁移吗?是需要2个小程序吗?

小程序迁移变更主体有什么作用?好多朋友都想做小程序迁移变更主体,但是又不太清楚具体有啥用,今天我就来详细说说。首先,小程序迁移变更主体最重要的作用就是可以修改主体。比如你的小程序原来是 A 公司的,现在 A 公司…

2024-5-9-从0到1手写配置中心Config之@ConfigurationProperties热更新

在PropertySourcesProcessor中,需要通过http从config-server获取配置。 使用ConfigMeta包装服务信息 在MidnightConfigService接口中添加默认实现类 继承MidnightRepositoryChangeListener接口;获取默认的MidnightRepository;创建MidnightCo…

12306技术内幕

公司内部做的一次技术分享 文章目录 12306的成就12306系统特点12306系统难点解决思路产品角度技术角度余票库存的表如何设计? 抢票软件推荐巨人的肩膀 对于未公开的技术部分,只能结合已公开的信息,去做大胆的猜想。 本文提到的一些解决方案&…

chatgpt线性差值 将直线渐变颜色

color(x)(x-x1)/(x2-x1) 与gpt给出的 这个位置比例可以表示为d/L是概念相同 x-x1是计算当前点距离起点距离,x2-x1是计算长度 例如,如果我们在直线上距离起点A的距离为d,整条直线的长度为L 用数学方式解释 2024/5/25 18:54:30 当我们要在一…

【算法例题】递推与递归

刚讲完递推和递归,趁热打铁,给大家出点例题。 没学过的,先去学一下。 【算法】递推&递归https://blog.csdn.net/yangyanbin_sam/article/details/139182393?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22…

⌈ 传知代码 ⌋ 基于扩散模型的无载体图像隐写术

💛前情提要💛 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间,对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

小红书无限加群脚本无需ROOT【使用简单无教程】

小红书无限加群脚本无需ROOT,包含了对应的小红书版本【使用简单无教程】 链接:https://pan.baidu.com/s/1HkLhahmHDFMKvqCC3Q3haA?pwd6hzf 提取码:6hzf

ISCC 2024 部分wp

文章目录 一、Misc1、Number_is_the_key2、FunZip3、擂台—— 重“隐”;4、RSA_KU5、时间刺客6、成语学习7、 精装四合一8、钢铁侠在解密9、有人让我给你带个话10、Magic_Keyboard11、工业互联网模拟仿真数据分析 二、Web1、还没想好名字的塔防游戏2、代码审计3、原…

【C++】<图形库> 三人成棋(面向对象写法)

目录 一、游戏需求 二、程序架构 三、代码实现 四、实现效果 五、已知BUG 一、游戏需求 构建一个五子棋游戏,在自定义棋盘宽度和高度的基础上,实现三人对战功能,并且能判定谁输谁赢。 二、程序架构 (1) 对象分析: 【1】 需…

人工智能-YOLOv10-行人和车辆检测-yolo改进测距测速代码和原理

YOLOv10: 实时端到端目标检测技术的全新突破 YOLOv10代表了实时目标检测领域的重大进展,是YOLO系列模型的最新迭代,专为追求极致效率与精度平衡的应用场景设计。此项目由北京大学机器智能研究组(THU-MIG)的Ao Wang、Hui Chen、Li…

直击CHIMA 2024│美创全栈全新数安能力精彩亮相

5月17日,中国医院信息网络大会(CHIMA 2024)在南京正式拉开帷幕。本次大会以新质生产力理论为指导,以深化应用,融合创新,用信息技术赋能医院高质量发展为主题。 作为医疗数据安全的创新引领者和深耕实践者,美创科技再次…

IDEA通过tomcat运行注意事项

配置run--》edit configurations 以下的A B部分要保持一致 A和B的路径要保持一致

vue/core源码中ref源码的js化

起源: 当看见reactivity文件中的ref.ts文件长达五百多的ts代码后,突发奇想想看下转化成js有多少行。 进行转化: let shouldTrack true; // Define shouldTrack variable let activeEffect null; // Define activeEffect variable// 定义…

MATLAB system identification系统辨识app的使用

系统辨识 前言系统辨识第一步 选取时域数据到app第二步 分割数据第三步 设置传递函数的参数第四步 Estimate第五步 结束 前言 接上节:simulink-仿真以及PID参数整定 系统模型的辨识工作,在控制领域,一般用于开发控制器的先手工作。一般而言…