瑞_23种设计模式_原型模式

news2024/11/18 23:24:08

文章目录

    • 1 原型模式(Prototype Pattern)
      • 原型模式的结构
    • 2 实现
    • 3 案例
      • 3.1 需求
      • 3.2 设计
      • 3.3 代码实现
        • 3.3.1 浅克隆代码实现
        • 3.3.2 深克隆代码实现
      • 3.4 总结

🙊 前言:本文章为瑞_系列专栏之《23种设计模式》的原型模式篇。本文中的部分图和概念等资料,来源于博主学习设计模式的相关网站《菜鸟教程 | 设计模式》和《黑马程序员Java设计模式详解》,特此注明。本文中涉及到的软件设计模式的概念、背景、优点、分类、以及UML图的基本知识和设计模式的6大法则等知识,建议阅读 《瑞_23种设计模式_概述》

本系列-设计模式-链接:《瑞_23种设计模式_概述》
本系列-单例模式-链接:《瑞_23种设计模式_单例模式》
本系列-工厂模式-链接:《瑞_23种设计模式_工厂模式》

在这里插入图片描述

1 原型模式(Prototype Pattern)

  原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

  这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

瑞:原型模式就是用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。所以本质上就是深浅克隆。

  • 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

  • 主要解决:在运行期建立和删除原型。

  • 何时使用
      1️⃣ 当一个系统应该独立于它的产品创建,构成和表示时。
      2️⃣ 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
      3️⃣ 为了避免创建一个与产品类层次平行的工厂类层次时。
      4️⃣ 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

  • 如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

  • 关键代码
      1️⃣ 实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。
      2️⃣ 原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

  • 应用实例
      1️⃣ 细胞分裂。
      2️⃣ JAVA 中的 Object clone() 方法。

  • 优点
      1️⃣ 性能提高。
      2️⃣ 逃避构造函数的约束。

  • 缺点
      1️⃣ 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
      2️⃣ 必须实现 Cloneable 接口。

  • 使用场景
      1️⃣ 资源优化场景。
      2️⃣ 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
      3️⃣ 性能和安全要求的场景。
      4️⃣ 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
      5️⃣ 一个对象多个修改者的场景。
      6️⃣ 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
      7️⃣ 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

  • 注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

原型模式的结构


  原型模式包含如下角色:

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

  接口类图如下:

在这里插入图片描述




2 实现

原型模式的克隆分为浅克隆和深克隆:

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

  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址

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

Realizetype(具体的原型类)
public class RealizeType implements Cloneable {

    public RealizeType() {
        System.out.println("具体的原型对象创建完成!");
    }

    @Override
    public RealizeType clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        return (RealizeType) super.clone();
    }
}
PrototypeTest(测试访问类)
public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建一个原型类对象
        RealizeType realizetype = new RealizeType();

        // 调用Realizetype类中的clone方法进行对象的克隆
        RealizeType clone = realizetype.clone();

        System.out.println("原型对象和克隆出来的是否是同一个对象?" + (realizetype == clone));
    }
}

  运行结果如下:

	具体的原型对象创建完成!
	具体原型复制成功!
	原型对象和克隆出来的是否是同一个对象?false



3 案例

3.1 需求

用原型模式生成“三好学生”奖状

  思路:同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同,可以使用原型模式复制多个“三好学生”奖状出来,然后在修改奖状上的名字即可。

3.2 设计

  类图如下:

在这里插入图片描述

3.3 代码实现

3.3.1 浅克隆代码实现

  代码如下:

学生类(类)

/**
 * 学生类 —— 浅克隆
 *
 * @author LiaoYuXing-Ray
 **/
public class Student {

    // 学生的姓名
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}
奖状类(类)
import java.time.LocalDate;

/**
 * 奖状类 —— 浅克隆
 *
 * @author LiaoYuXing-Ray
 **/
public class Citation implements Cloneable {
    private String name;

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

    public String getName() {
        return (this.name);
    }

    public void show() {
        System.out.println(student.getName() + "同学:在[" + LocalDate.now().getYear() + "]学年中表现优秀,被Ray评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}
测试类
/**
 * 测试类 —— 浅克隆
 *
 * @author LiaoYuXing-Ray
 **/
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 1.创建原型对象
        Citation citation = new Citation();
        // 创建张三学生对象
        Student stu = new Student();
        stu.setName("张三");
        citation.setStudent(stu);

        // 2.克隆奖状对象
        Citation citation1 = citation.clone();
        Student stu1 = citation1.getStudent();
        stu1.setName("李四");

        // 3.调用show方法展示
        citation.show();
        citation1.show();

        // 判断stu对象和stu1对象是否是同一个对象
        System.out.println("stu和stu1是同一个对象?" + (stu == stu1));

        // 浅克隆效果进一步演示
        stu.setName("Ray");
        citation.show();
        citation1.show();
    }
}

  运行结果如下:

	李四同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!
	李四同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!
	stu和stu1是同一个对象?true
	Ray同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!
	Ray同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!

  说明:
  stu对象stu1对象是同一个对象,就会产生将stu1对象中name属性值改为“李四”,两个Citation(奖状)对象中显示的都是李四的现象,继续验证,对stu对象中name属性值改为“Ray”,两个Citation(奖状)对象中显示的都是Ray。这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。如果不希望这种情况出现,需要使用深克隆,进行深克隆需要使用对象流。

3.3.2 深克隆代码实现

  代码如下:

学生类(类)
import java.io.Serializable;

/**
 * 学生类
 *
 * @author LiaoYuXing-Ray
 **/
public class Student implements Serializable {

    // 学生的姓名
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

奖状类(类)
import java.io.Serializable;
import java.time.LocalDate;

/**
 * 奖状类 —— 深克隆
 *
 * @author LiaoYuXing-Ray
 **/
public class Citation implements Cloneable, Serializable {

    private Student student;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student stu) {
        this.student = stu;
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }

    public void show() {
        System.out.println(student.getName() + "同学:在[" + LocalDate.now().getYear() + "]学年中表现优秀,被Ray评为三好学生。特发此状!");
    }
}
测试类


import java.io.*;

/**
 * 测试类 —— 深克隆
 *
 * @author LiaoYuXing-Ray
 **/
public class CitationTest {
    public static void main(String[] args) throws Exception {
        // 1.创建原型对象
        Citation citation = new Citation();
        // 创建张三学生对象
        Student stu = new Student();
        stu.setName("张三");
        citation.setStudent(stu);
        // 获取当前文件的绝对路径
        String absolutePath = System.getProperty("user.dir");
        String filePath = absolutePath + File.separator + "CitationTest.txt";
        // 创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        // 写对象
        oos.writeObject(citation);
        // 释放资源
        oos.close();

        // 创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        // 读取对象
        Citation citation1 = (Citation) ois.readObject();
        // 释放资源
        ois.close();
        Student stu1 = citation1.getStudent();
        stu1.setName("李四");

        citation.show();
        citation1.show();

        // 判断stu对象和stu1对象是否是同一个对象
        System.out.println("stu和stu1是同一个对象?" + (stu == stu1));

        // 深克隆效果进一步演示
        stu.setName("Ray001");
        stu1.setName("Ray002");
        citation.show();
        citation1.show();
    }
}

注:
  1️⃣以上代码主要是为了演示深克隆的效果,对流的处理并不规范
  2️⃣Citation类和Student类必须实现Serializable接口,否则会抛NotSerializableException异常。

  运行结果如下:

	张三同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!
	李四同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!
	stu和stu1是同一个对象?false
	Ray001同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!
	Ray002同学:在[2024]学年中表现优秀,被Ray评为三好学生。特发此状!

3.4 总结

  通过以上浅克隆和深克隆的实现结果可以得出,如果有一个Citation对象,其中包含了一个Student对象的引用:
  浅克隆生成的新Student对象会和原对象共享同一个Student对象,因为浅克隆不会复制被引用的对象,而是复制引用本身,也就是说,新对象和原对象会共享相同的被引用对象
  而深克隆则会为新Citation对象创建一个独立的Student对象副本。这样,对原对象或其任何被引用对象的修改都不会影响到克隆出来的对象

  总的来说:浅克隆适用于不需要复制引用类型属性的场景,实现起来相对简单;而深克隆则用在需要完全独立的对象副本的场景,实现起来较为复杂,但能确保原对象和克隆出的对象互不影响。




本文是博主的粗浅理解,可能存在一些错误或不完善之处,如有遗漏或错误欢迎各位补充,谢谢

  如果觉得这篇文章对您有所帮助的话,请动动小手点波关注💗,你的点赞👍收藏⭐️转发🔗评论📝都是对博主最好的支持~


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

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

相关文章

通用缓存SpringCache

概述 在项目中,我们通常会把高频的查询进行缓存。如资讯网站首页的文章列表、电商网站首页的商品列表、微博等社交媒体热搜的文章等等,当大量的用户发起查询时,借助缓存提高查询效率,同时减轻数据库压力。 目前的缓存框架有很多:…

【Linux C | 网络编程】netstat 命令图文详解 | 查看网络连接、查看路由表、查看统计数据

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…

深度学习快速入门--7天做项目

深度学习快速入门--7天做项目 0. 引言1. 本文内容2. 深度学习是什么3. 项目是一个很好的切入点4. 7天做项目4.1 第一天:数据整理4.2 第二天:数据处理4.3 第三天:简单神经网络设计4.4 第四天:分析效果与原因4.5 第五天:…

day02.C++命名空间

目录 一、命名空间的作用 二、命名空间的定义 三、命名空间的镶嵌定义 四、命名空间的使用方法 一、命名空间的作用 一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,不可避免地会出现变量或函数的命名冲突。当所有人的代码都测试通过…

iOS应用崩溃了,如何通过崩溃手机连接电脑查找日志方法

在iOS应用开发过程中,调试日志和奔溃日志是开发者必不可少的工具。当iOS手机崩溃时,我们可以连接电脑并使用Xcode Console等工具来查看日志。然而,这种方式可能不够方便,并且处理奔溃日志也相当繁琐。克魔助手的出现为开发者带来了…

Day 17------C语言收尾之链表的删除、位运算、预处理、宏定义

链表 空链表: 注意:函数不能返回局部变量的地址 操作: 1.创建空链表 2.头插 3.尾插 4.链表遍历 5.链表的长度 free:释放 删除: 头删 void popFront(struct Node *head) { //1.p指针变量指向首节点 //2.断…

康姿百德床垫价格合理功效好,用科技力量守护您的睡眠健康

现代生活中,优质睡眠的观念已深入人心。人们渐渐认识到,一个舒适的床垫不仅仅是睡眠的工具,更是健康的守护者。很多朋友在选购床垫一掷千金,却找不到一款合适的床垫。康姿百德床垫是专为提升睡眠质量研发的床垫,成为了…

网络安全全栈培训笔记(60-服务攻防-中间件安全CVE复现WeblogicJenkinsGlassFish)

第60天 服务攻防-中间件安全&CVE复现&Weblogic&Jenkins&GlassFish 知识点: 中间件及框架列表: lIS,Apache,Nginx,Tomcat,Docker,Weblogic,JBoos,WebSphere,Jenkins, GlassFish,Jira,Struts2,Laravel,Solr,Shiro,Thinkphp,Sprng,Flask,jQuery 1、中间件-Web…

Portainer访问远程Docker (TLS加密)

前言: docker的2375端口,出于安全性考虑即(Docker Remote API未授权访问漏洞),是不开放的,如果想要管理远程docker,可以使用TLS机制来进行访问,这里以Portainer访问连接为例 文章参考:https://b…

外卖,也可以“聚合”

文章首发于微信公众号:PenguinPay ,欢迎关注。 一、背景 1.1 订单来源 在过去,商家普遍使用传统POS收银软件进行线下店面收银,可以在一定程度上提升收银效率。 之后随着O2O外卖渠道的发展,越来越多的商家选择在线上平台运营门店…

吸猫毛空气净化器哪个好?推荐除猫毛效果好的宠物空气净化器品牌

如今,越来越多的家庭选择养宠物,使家庭变得更加温馨。然而,养宠物可能会带来异味和空气中的毛发增多,这可能会成为一大困扰,并对健康造成问题。 为了不让家里充斥着异味,特别是来自宠物便便的味道&#xf…

DATAX改造支持geometry类型数据同步

数据库使用postgresql安装了postgis插件存储了geometry空间数据,想使用datax做数据同步,但datax本身不支持geometry类型数据,如何改造呢? 1.首先下载已改造支持geometry类型的datax引擎,下载地址 https://download.c…

《区块链简易速速上手小册》第5章:智能合约(2024 最新版)

文章目录 5.1 智能合约的概念5.1.1 智能合约的基础知识5.1.2 主要案例:去中心化金融(DeFi)平台5.1.3 拓展案例 1:智能合约在供应链管理中的应用5.1.4 拓展案例 2:智能合约在房地产交易中的应用 5.2 智能合约的应用案例…

函数重载你真的了解吗?

1.什么叫函数重载? 函数重载(Function Overloading)是指在同一个作用域内,允许定义多个具有相同名称但参数列表不同的函数。具体而言,函数重载允许你定义同名的函数,但这些函数应该有不同的参数类型、参数个…

代码随想录算法训练营Day44|完全背包理论基础、518.零钱兑换II、377. 组合总和 Ⅳ

目录 完全背包理论基础 完全背包问题 算法实现 518.零钱兑换II 前言 思路 377. 组合总和 Ⅳ 前言 思路 算法实现 总结 完全背包理论基础 题目链接 文章链接 完全背包问题 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是…

计网——应用层

应用层 应用层协议原理 网络应用的体系结构 客户-服务器(C/S)体系结构 对等体(P2P)体系结构 C/S和P2P体系结构的混合体 客户-服务器(C/S)体系结构 服务器 服务器是一台一直运行的主机,需…

springboot 整合 PowerJob实现定时任务调度

最近项目需要使用定时任务,而使用了PowerJob做任务调度模块,感觉这个框架真香,今天我们就来深入了解一下新一代的定时任务框架——PowerJob! 简介 PowerJob是基于java开发的企业级的分布式任务调度平台,与xxl-job一样…

关于破解IDEA后启动闪退的问题

问题描述:2023.1启动不了,双击桌面图标,没有响应。 解决办法: 打开C:\Users\c\AppData\Roaming\JetBrains\IntelliJIdea2023.1\idea64.exe.vmoptions 这个文件。 内容如下所示: 删除红框的数据以后,再登录…

ARM架构可视化ROS消息方案部署

ARM架构可视化ROS消息方案部署 三种方案, 1. webviz 2. foxglove 3. rosviz 注: web要用firefox, chromimum用不了, 可能是因为取消了时间同步机制的原因 先说三种方案的优劣, webviz 延迟比较高, 但是部署相对简单, foxglove 部署比较费劲, 但是效果不错, 延迟低, 本文会尽…

MySQL篇----第二篇

系列文章目录 文章目录 系列文章目录前言一、MyIASM二、Memory三、数据库引擎有哪些前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、MyIASM MyIASM是 MySQL默…