Java 设计模式——原型模式

目录

  • 1.概述
  • 2.结构
  • 3.实现
    • 3.1.浅拷贝
    • 3.2.深拷贝
      • 3.2.1.通过对象序列化实现深拷贝(推荐)
      • 3.2.2.重写 clone() 方法来实现深拷贝
  • 4.优缺点
  • 5.使用场景

1.概述

(1)原型模式 (Prototype Pattern) 是一种创建型设计模式,是通过复制(克隆)现有对象来创建新对象的模式。它允许我们创建一个原型对象,然后通过复制该原型对象来创建新的对象,而无需通过实例化类来创建新的对象

(2)在原型模式中,一个类将自身的实例作为原型,通过复制这个原型来创建新的对象。这个过程隐藏了对象的创建细节,而且可以提高创建对象的效率。

(3)原型模式基于对象的复制,可以分为浅拷贝和深拷贝两种形式:

  • 浅拷贝:复制对象时,仅仅复制对象的引用,而不复制对象本身。新对象和原对象共享同一个引用,修改一个对象会影响到另一个对象。
  • 深拷贝:复制对象时,不仅复制对象的引用,还复制对象本身,创建一个独立的对象。新对象和原对象互不影响,修改一个对象不会影响到另一个对象。

(4)在实现原型模式时,通常需要实现一个 Cloneable 接口(或类似的机制),该接口标识类具有复制自己的能力。具体的实现细节则根据语言和框架的不同而有所差异。

2.结构

(1)原型模式包含如下角色:

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

(2)接口类图如下:
在这里插入图片描述

3.实现

Java 中的 Object 类中提供了 clone() 方法来实现浅克隆Cloneable 接口是上面的类图中的抽象原型类,而实现了 Cloneable 接口的子实现类就是具体的原型类。

3.1.浅拷贝

Address.java

public class Address {
    int id;
    String addressName;
    
    public Address(int id, String addressName) {
        this.id = id;
        this.addressName = addressName;
    }
}

Person.java

public class Person implements Cloneable{
    private int id;
    private String name;
    private Address address;
    
    public String getName() {
        return name;
    }
    
    public Person(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
        System.out.println("具体的原型对象创建完成!");
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        return (Person) super.clone();
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
    
}

Client.java

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person(1, "小华", new Address(1, "北京"));
        Person person2 = (Person) person1.clone();
        System.out.println("person1: " + person1);
        System.out.println("person2: " + person2);
        System.out.println("person1.getName() == person2.getName() 的结果为: " + (person1.getName() == person2.getName()));
        System.out.println("person1 == person2 的结果为: " + (person1 == person2));
    }
}

输出结果如下图所示:

在这里插入图片描述

3.2.深拷贝

3.2.1.通过对象序列化实现深拷贝(推荐)

先将 Address 类和 Person 类实现 Serializable 接口,然后再修改 Client.java 中的代码即可。

Client.java

public class Client {
    public static void main(String[] args) throws Exception {
        Person person1 = new Person(1,"小华",new Address(1,"北京"));
    
        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\testData\\a.txt"));
        //写对象
        oos.writeObject(person1);
        //释放资源
        oos.close();
    
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\testData\\a.txt"));
        //读取对象
        Person person2 = (Person) ois.readObject();
        //释放资源
        ois.close();
        
        System.out.println("person1:"+person1);
        System.out.println("person2:"+person2);
        System.out.println("person1.getName() == person2.getName()的结果为:"+(person1.getName() == person2.getName()));
        System.out.println("person1 == person2的结果为:"+(person1 == person2));
    }
}

在这里插入图片描述

3.2.2.重写 clone() 方法来实现深拷贝

public class Person implements Cloneable {
    private int id;
    private String name;
    private Address address;
    
    public String getName() {
        return name;
    }
    
    public Person(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
        System.out.println("具体的原型对象创建完成!");
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        Person person = null;
        person = (Person)super.clone();
        //对引用数据类型单独处理
        person.name = new String(name);
        person.address = (Address)address.clone();
        return person;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

4.优缺点

(1)原型模式具有以下优点:

  • 减少对象的创建成本:原型模式通过复制现有对象来创建新对象,避免了使用构造函数的开销,提高了对象的创建效率。
  • 简化对象的创建过程:使用原型模式,不需要关心对象的具体实现细节,只需复制一个现有对象即可得到新的对象,简化了对象的创建过程。
  • 支持动态添加和修改对象的属性:可以通过修改现有对象的属性来创建新对象,避免了在代码中显式设置对象属性的麻烦。
  • 提供了一种可扩展的创建方式:原型模式通过复制现有对象,可以创建出多个具有相同属性的对象,也可以根据需要修改部分属性,从而具有更高的灵活性和可扩展性。

(2)原型模式也存在一些缺点:

  • 需要实现 Cloneable 接口或类似的机制:在一些语言和框架中,实现原型模式需要实现 Cloneable 接口或类似的机制,这对于某些编程语言来说可能需要额外的工作。
  • 深拷贝可能较为复杂:如果需要复制的对象存在引用类型的成员变量,深拷贝可能需要额外的处理来确保所有引用对象也能被正确地复制。
  • 对象复杂性限制:对于包含循环引用或复杂对象图的对象,可能会导致复制过程变得复杂或不可行。

(3)综上所述,原型模式在某些情况下可以提供高效、灵活和可扩展的对象创建方式,但使用时需注意克隆的实现细节和对象的复杂性。

5.使用场景

(1)原型模式适用于以下场景:

  • 对象创建成本高:当对象的创建成本较高时,例如需要进行复杂的计算、数据读取或网络请求等操作,可以使用原型模式复制现有对象来提高创建效率。
  • 对象初始化复杂:当对象的初始化过程较为复杂,包含多个步骤或依赖关系较多时,可以使用原型模式来复制一个已经初始化好的对象,避免重新执行初始化过程。
  • 动态创建和定制对象:当需要根据一些条件动态创建和定制对象时,原型模式可以提供一种更加灵活的创建方式。通过复制现有对象并根据需要修改部分属性,即可创建出符合条件的新对象。
  • 避免构造函数调用:在某些场景下,使用构造函数创建对象可能不可行或不符合需要。例如,某些框架中对象的创建由框架负责,通过原型模式可以避免直接调用构造函数。
  • 对象的状态变化较小:当对象的状态变化较小,只是部分属性需要修改时,使用原型模式可以避免重新创建对象,提高了性能。

(2)总的来说,原型模式适用于需要复制和创建新对象的场景,尤其是在对象的创建成本较高或初始化复杂的情况下,使用原型模式可以提高创建效率和灵活性。

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

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

相关文章

网页开发基础——HTML

一、flask框架 Flask是一种轻量级的Python web应用程序框架,可以帮助使用者快速构建Web应用程序和API。由于其简洁、灵活和易于上手的特点,Flask被广泛用于开发小型到中型的Web应用程序和后端API。本次我们主要是使用flask框架,进行一个小型w…

知网的caj格式怎么转化成pdf格式?两个方法简单快捷!

在使用知网等学术资源时,我们常常会遇到CAJ格式的文件,然而CAJ格式并不是常见的文件格式,给我们的查阅和分享带来一些不便。为了更方便地处理这些文件,我们可以将其转换为常见的PDF格式。在本文中,我将为您介绍两种简单…

超标量处理器寄存器rename

1.相关性介绍 在CPU中,一段程序会被编译成一连串的汇编指令,指令与指令之间可能会具有相关性(dependency)。所谓相关性,即一条指令的执行会依赖于另一条指令的结果,相关性可以分为:① 数据相关性…

Pycharm中如何设置在新窗口打开项目

settingAppearance&Behavior–System SettingsOpen project in - new window

年出货2亿台,只赚“辛苦钱”!又一家代工巨头押宝汽车电子

7月20日,作为国内ODM龙头之一的华勤技术正式启动招股,拟在上交所主板上市。此前,因技术先进性、科创属性不足等问题,该公司终止科创板IPO。 华勤技术成立于2005年,几年后赶上了全球智能手机的黄金时代,招股…

系统集成|第三章(笔记)

目录 第三章 系统集成专业技术3.1 信息系统建设3.1.1 信息系统3.1.2 信息系统集成 3.2 信息系统设计3.3 软件工程3.4 面向对象系统分析与设计3.5 软件架构3.5.1 软件的架构模式3.5.2 软件中间件 3.6 典型应用集成技术3.6.1 数据库与数据仓库技术3.6.2 Web Services 技术3.6.3 J…

3 Linux基础篇-VMware和Linux的安装

3 Linux基础篇-VMware和Linux的安装 文章目录 3 Linux基础篇-VMware和Linux的安装3.1 安装VMware和CentOS3.1.1 VM安装3.1.2 Centos7.6的安装步骤 3.3 虚拟机基本操作3.4 安装VMtools3.5 设置共享文件夹 学习视频来自于B站【小白入门 通俗易懂】2021韩顺平 一周学会Linux。可能…

无涯教程-jQuery - unbind()方法函数

unbind([type],[fn])方法的作用与bind相反,它从每个匹配的元素中删除绑定事件。 unbind( [type], [fn] ) - 语法 selector.unbind( [type], [fn] ) 这是此方法使用的所有参数的描述- type - 一种或多种事件类型,以空格分隔。 fn …

rust学习-智能指针

适用场景 有一个在编译时未知大小的类型,想在需要确切大小的上下文使用该类型值 示例1 无意义的例子:将一个单独的值存放在堆上并不是很有意义,b更应该放到栈上 fn main() {let b Box::new(5);// box 在 main 的末尾离开作用域时&#x…

Visual Studio 2022 程序员必须知道高效调试手段与技巧(中)

🎬 鸽芷咕:个人主页 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活! 文章目录 📋 前言💬 调试的时候查看程序当前信息💭 查看临时变量的值💭 查…

NISP二级考试安排(2023年7月-12月)

国家信息安全水平考试(NISP)认证分为一级和二级,证书由 中国信息安全测评中心 颁发,只有考取NISP一级证书才能考取NISP二级。NISP与CISP无缝对接。CISP需要工作经验所以在校生无法报考,NISP填补了在校大学生无法考取CI…

【TI毫米波雷达笔记】IWR6843AOP工程模板创建 cannot find file “libsleep_xwr68xx.aer4f“等解决方案

【TI毫米波雷达笔记】IWR6843AOP工程模板 cannot find file “libsleep_xwr68xx.aer4f” 解决方案 我在建立工程时 发现了一个问题 参考: blog.csdn.net/qq_16660871/article/details/126246572报错为 cannot find file "libsleep_xwr68xx.aer4f"最后检…

在命令行模式、eclipse console下执行Java程序输入中文的几种情况尝试

介绍 在命令行模式下执行Java程序,如果输入中文,经常会出现和代码中的解码字符集不匹配的情况,导致结果不正确。 在命令行模式下执行Java程序,输入中文,其实是用某种字符集编码成字节流,Java程序读取该字节…

Tomcat修改端口号

网上的教程都比较老,今天用tomcat9.0记录一下 conf文件夹下server.xml文件 刚开始改了打红叉的地方,发现没用,改了上面那行

【爬虫逆向案例】某道翻译js逆向—— sign解密

声明:本文只作学习研究,禁止用于非法用途,否则后果自负,如有侵权,请告知删除,谢谢! 【爬虫逆向案例】某道翻译js逆向—— sign解密 1、前言2、步骤3、源码4、号外 1、前言 相信各位小伙伴在写…

Unity Shader - if 和 keyword 的指令比较

文章目录 环境TestingIf4Sampleunity shaderlab 中的 TestingIf4Sample.shadergraphics analyzer 中的 TestingIf4Sample.glsl TestingKW4Sampleunity shaderlab 中的 TestingKW4Sample.shadergraphics analyzer 中的 TestingKW4Sample.glsl 比较 环境 Unity : 2020.3.37f1 Pi…

如何测试Linux内核

目录 概述 LTP 构建系统 C测试用例 参考资料 Autotest Kmemleak Kmemcheck Linaro LAVA 调试器 GDB KGDB 设备驱动测试 资料获取方法 概述 在本文中,我们将讨论用于测试Linux内核的各种框架和工具。首先,我们将介绍LTP( Linux Test Proje…

幸福长寿的秘诀 —— 查理芒格

查理芒格:幸福长寿的秘诀其实很简单。_哔哩哔哩_bilibili People trying to figure out what the secret to life, is to a long and happy life ? Its simple. You dont have a lot of envy. You dont have a lot of resentment. You dont overspend your incom…

安全DNS,状态码,编码笔记整理

一 DNS DNS(Domain Name System)是互联网中用于将域名转换为IP地址的系统。 DNS的主要功能包括以下几个方面: 域名解析:DNS最主要的功能是将用户输入的域名解析为对应的IP地址。当用户在浏览器中输入一个域名时,操作…

PostgreSQL-Character with value 0x09 must be escaped.

在使用json相关函数时,报了这个错: Character with value 0x09 must be escaped.中文即使:值为0x09的字符必须转义。 找了下这个0x09 这个ASCII的值,是水平制表符。那这应该是因为json不支持换行导致的,我们将水平制…