当前位置: 首页 > news >正文

二十三种设计模式第五篇--原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。
我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

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

在什么情况下使用原型模式:

  1. 在类的初始化消耗的资源非常之多的时候使用
  2. 使用new创建一个对象需要非常烦琐的过程
  3. 构造函数比较复杂
  4. 在循环体中产生大量对象

注意原型模式中有使用克隆操作来实现复制类,但是clone操作又可以分为深克隆和浅克隆之分,既然要用到clone操作,我们知道深拷贝需要实现Cloneable,Serializable两个接口,重写clone方法,并且在java中,我们主要使用的clone操作都是浅拷贝。
其深拷贝具体方法:

  1. 实现 Cloneable 接口,递归 clone 引用对象或 new 新对象(类的属性字段未实现 Cloneable 接口)
  2. 借助序列化完成深拷贝,如实现 JDK java.io.Serializable 接口、json格式序列化、xml格式序列化等。

原型模式UML图

这里我从软件设计师书本上摘抄到一个例子来介绍:
在这里插入图片描述
Prototype模式适用于:

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

原型模式代码

根据上述UML图,我们来具体实现:

public class Main {public static void main(String[] args) {Product product1 = new Product(2023, 5.8);System.out.println(product1.getId() + " " + product1.getPrice());// Product product2 = new Product(2023, 5.8);Product product2 = (Product) product1.Clone();System.out.println(product2.getId() + " " + product2.getPrice());Product product3 = (Product) product1.Clone();System.out.println(product3.getId() + " " + product3.getPrice());}
}
interface Prototype {public Object Clone();
}
class Product implements Prototype {private int id;private double price;//创建无参构造方法public Product() {}//有参构造public Product(int id, double price) {this.id = id;this.price = price;}public int getId() {return id;}public double getPrice() {return price;}@Overridepublic Object Clone() {Product object = new Product();object.id = this.id;object.price = this.price;return object;}
}

通过上述方式,我们就已经简单实现一个原型模式了。

第二个实例实现原型模式

在这里插入图片描述
这个图应该看得懂吧?孙大圣以及金箍棒,首先孙悟空是猴子,他有个绰号叫齐天大圣,我们在齐天大圣里边实现原型模式。

public class Monkey {public int height;public int weight;   //基本型数据public Date birthday;}

创建金箍棒,实现序列化接口

//金箍棒
public class JinGuBang implements Serializable {public float h = 100;public float d = 10;//金箍棒变大public void big(){this.d *= 2;this.h *= 2;}
//金箍棒变小public void small(){this.d /= 2;this.h /= 2;}@Overridepublic String toString() {return "JinGuBang{" +"h=" + h +", d=" + d +'}';}
}
public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {public JinGuBang jinGuBang;Object obj;//先初始化一些数值public  QiTianDaSheng(){//只是初始化this.birthday = new Date();this.jinGuBang = new JinGuBang();}//将原来的浅克隆改为深克隆.@Overrideprotected Object clone() throws CloneNotSupportedException {//  return super.clone();// return this.shallowClone(this);   //浅克隆return this.deepClone();   //深克隆}//利用对象流完成深克隆,       还有一种麻烦的实现: 递 归./*深复制把要复制的对象所引用的对象都复制了一遍。拷贝需要实现Cloneable, Serializable两个接口,重写clone方法*/public Object deepClone(){try{ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);oos.flush();ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();copy.birthday = new Date();return copy;}catch (Exception e){e.printStackTrace();return null;}}//浅克隆public QiTianDaSheng shallowClone(QiTianDaSheng target){QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();qiTianDaSheng.height = target.height;qiTianDaSheng.weight = target.weight;//浅克隆对对象类型的数据只克隆了地址,没有复制值qiTianDaSheng.jinGuBang = target.jinGuBang;    //对于引用型的数据,只克隆了地址.qiTianDaSheng.birthday = new Date();return  qiTianDaSheng;}@Overridepublic String toString() {return "QiTianDaSheng{" +"jinGuBang=" + jinGuBang +", height=" + height +", weight=" + weight +", birthday=" + birthday +'}';}
}
public class DeepCloneTest {public static void main(String[] args) {QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();try {QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();System.out.println(   "克隆:"+   clone);System.out.println(     qiTianDaSheng );//原型模式下对象创建了两次,但值 一样.System.out.println(   "hashcode:  " +  clone.hashCode()+"\t"+ qiTianDaSheng.hashCode() );//jingubang是一个引用型, 对于浅克隆.System.out.println(  "jingubang的hashcode: "+ clone.jinGuBang.hashCode() +"\t"+   qiTianDaSheng.jinGuBang.hashCode() );//  System.out.println("判断克隆结果,如两个jinGuBang对象相同,则为浅克隆,如不同,则为深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));} catch (Exception e) {e.printStackTrace();}//        QiTianDaSheng q = new QiTianDaSheng();
//        QiTianDaSheng n = q.shallowClone(q);
//        System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));}
}

至此,我们通过齐天大圣类来完成类的克隆,并且实现序列化接口和Cloneable接口,完成深克隆操作,来实现原型模式。如果以上介绍还不是看的很爽,那我们接着看。

第三个实例实现原型模式

老样子,先摆上UML图。
在这里插入图片描述
创建一个shape类作为父类

public abstract class Shape implements Cloneable {private String id;protected String type;public List values;//注意关键是在 这里,应该重写一个clone()方法,提供对象克隆功能。默认是一个浅克隆.@Overridepublic Object clone() {Object clone = null;try {clone = super.clone();    ///} catch (CloneNotSupportedException e) {e.printStackTrace();}return clone;}abstract void draw();public void setValues(List values) {this.values = values;}public List getValues() {return values;}public String getType(){return type;}public String getId() {return id;}public void setId(String id) {this.id = id;}
}

创建子类

public class Circle extends Shape implements Cloneable{public Circle(){type = "Circle";}@Overridepublic void draw() {System.out.println("Inside Circle::draw() method.");}
}
public class Rectangle extends Shape {public Rectangle(){type = "Rectangle";}@Overridepublic void draw() {System.out.println("Inside Rectangle::draw() method.");}
}
public class Square extends Shape {public Square(){type = "Square";}@Overridepublic void draw() {System.out.println("Inside Square::draw() method.");}
}

创建好克隆类

public class ShapeCache {//用一个容器存好原始对象.private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();//如果要获取某个对象的一个拷贝,则从容器中取出原型, 再调用原型的拷贝方法public static Shape getShape(String shapeId) {Shape cachedShape = shapeMap.get(shapeId);return (Shape) cachedShape.clone();}// 对每种形状都运行数据库查询,并创建该形状// shapeMap.put(shapeKey, shape);// 例如,我们要添加三种形状( 这就是三种形状的原型, 以用于后期的拷贝 )public static void loadCache() {Circle circle = new Circle();circle.setId("1");List list=new ArrayList();list.add(1);list.add(2);circle.setValues(  list   );shapeMap.put(circle.getId(),circle);Square square = new Square();square.setId("2");shapeMap.put(square.getId(),square);Rectangle rectangle = new Rectangle();rectangle.setId("3");shapeMap.put(rectangle.getId(),rectangle);}
}

创建测试类

public class PrototypePatternDemo {public static void main(String[] args) {ShapeCache.loadCache();  //首先加载 类的原型实例.//以下操作获取原型的克隆对象.Shape clonedShape = (Shape) ShapeCache.getShape("1");//在这里可以对这个对象进行值 的设置,想像一下,如果这个对象有多个值 要设置的话,避免了创建对象的消耗.System.out.println("Shape : " + clonedShape.getType()+"  "+clonedShape);Shape cs2 = (Shape) ShapeCache.getShape("1");//在这里可以对这个对象进行值 的设置,想像一下,如果这个对象有多个值 要设置的话,避免了创建对象的消耗.cs2.setId("333");// *******注意两次获取的对象不同,这叫原型模式System.out.println("Shape : " + cs2.getType()+"   "+cs2 );Shape clonedShape2 = (Shape) ShapeCache.getShape("2");System.out.println("Shape : " + clonedShape2.getType());        Shape clonedShape3 = (Shape) ShapeCache.getShape("3");System.out.println("Shape : " + clonedShape3.getType());//默认情况下: clone()是一个浅克隆.System.out.println(  "两次产生的对象中的 引用类型的属性值(地址) 是相等的:"+  ( clonedShape.values==cs2.values )  );//这说明: 当对clonedShape中的 values进行修改时, cs2.values也会变,这肯定是不被 允许 .clonedShape.values.add("abc");  //  cs2.values也会变for(   Object o:cs2.values){System.out.println(   o );}// 下面改为深克隆来完成对对象属性值 的复制.}
}

相关文章:

二十三种设计模式第五篇--原型模式

原型模式&#xff08;Prototype Pattern&#xff09;是用于创建重复的对象&#xff0c;同时又能保证性能。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式是实现了一个原型接口&#xff0c;该接口用于创建当前对象的克隆。当直接创建…...

阿里云镜像区别公共镜像、自定义、共享、云市场和社区镜像介绍

阿里云服务器镜像根据来源不同分为公共镜像、自定义镜像、共享镜像、云市场镜像和社区镜像&#xff0c;一般没有特殊情况选择公共镜像&#xff0c;公共镜像是阿里云官网提供的正版授权操作系统&#xff0c;云市场镜像是在纯净版操作系统的基础上预装了相关软件及运行环境&#…...

非线性方程二分法

非线性方程二分法 优点&#xff1a;算法直观、简单、总能保证收敛&#xff1b;局限&#xff1a;收敛速度慢、一般不单独用它求根&#xff0c;仅为了获取根的粗略近似 文章目录 非线性方程二分法[toc]1 二分法基本思想2 二分法实现 1 二分法基本思想 设 f ( x ) f(x) f(x)在 [ …...

H3C防火墙单机旁路部署(网关在防火墙)

防火墙旁路部署在核心交换机上&#xff0c;内网有三个网段vlan 10&#xff1a;172.16.10.1/24、vlan 20&#xff1a;172.16.20.1/24、vlan30&#xff1a;172.16.30.1。要求内网网关在防火墙设备上&#xff0c;由防火墙作为DHCP服务器给终端下发地址&#xff0c;同时由防火墙来控…...

基于密度的无线传感器网络聚类算法的博弈分析(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 提高能源效率是无线传感器网络面临的关键挑战之一&#xff0c;无线传感器网络日益普遍。由于节点&#xff08;传感器&#xff…...

宕机了?!DolphinScheduler 高可用和 Failover 机制关键时刻保命

高可用性是 Apache DolphinScheduler 的特性之一。它通过冗余来避免单点问题&#xff0c;所有组件天然支持横向扩容&#xff1b;但仅仅保证了冗余还不够&#xff0c;当系统中有节点宕机时&#xff0c;还需要有故障转移机制能够自动将宕机节点正在处理的工作转移到新节点上执行&…...

try(){}用法try-with-resources、try-catch-finally

属于Java7的新特性。 经常会用try-catch来捕获有可能抛出异常的代码。如果其中还涉及到资源的使用的话&#xff0c;最后在finally块中显示的释放掉有可能被占用的资源。 但是如果资源类已经实现了AutoCloseable这个接口的话&#xff0c;可以在try()括号中可以写操作资源的语句(…...

常见Http错误码学习

​ 常见 http 错误码 服务器巡检时比较常见的 http 错误码 400 Bad Request408 Request Timeout499 client has closed connection502 Bad Gateway504 Gateway Timeout 这些错误码反映了服务器什么样的状态&#xff0c;仅看字面意思还不太容易理解&#xff0c;就动手做个试验…...

qemu-基础篇——ARM 链接过程分析(六)

文章目录 ARM 链接过程分析源文件global_bss_file.cglobal_data_fle.cglobal_function_file.cglobal_rodata_file.cmain.c 链接文件 link.lds编译命令及反汇编命令解析 .o 文件global_bss_file.oglobal_data_fle.oglobal_function_file.oglobal_rodata_file.omain.o 链接观察链…...

Java企业工程项目管理系统+spring cloud 系统管理+java 系统设置+二次开发

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…...

Eureka与Zookeeper的区别

著名的CAP 理论指出&#xff0c;一个分布式系统不可能同时满足 C( 一致性 ) 、 A( 可用性 ) 和 P( 分区容错性 ) 。 由于分区容错性在是分布式系统中必须要保证的&#xff0c;因此我们只能在 A 和 C 之间进行权衡&#xff0c;在此 Zookeeper 保证的是 CP, 而 Eureka 则是 AP…...

顺序表和链表的各种代码实现

一、线性表 在日常生活中&#xff0c;线性表的例子比比皆是。例如&#xff0c;26个英文字母的字母表&#xff08;A,B,C,……&#xff0c;Z&#xff09;是一个线性表&#xff0c;表中的数据元素式单个字母。在稍复杂的线性表中&#xff0c;一个数据元素可以包含若干个数据项。例…...

C# 介绍三种不同组件创建PDF文档的方式

1 c# 数据保存为PDF&#xff08;一&#xff09; &#xff08;spire pdf篇&#xff09; 2 c# 数据保存为PDF&#xff08;二&#xff09; &#xff08;Aspose pdf篇&#xff09; 3 c# 数据保存为PDF&#xff08;三&#xff09; &#xff08;PdfSharp篇&#xff09; 组件名称 绘制…...

极简面试题 --- Redis

什么是 Redis&#xff1f; Redis 是一个基于内存的键值存储系统&#xff0c;也被称为数据结构服务器。它支持多种数据结构&#xff0c;例如字符串、哈希表、列表、集合和有序集合&#xff0c;并且可以在内存中快速读写。 Redis 的优势有哪些&#xff1f; 快速&#xff1a;由…...

可视化图表API格式要求有哪些?Sugar BI详细代码示例(4)

Sugar BI中的每个图表可以对应一个数据 API&#xff0c;用户浏览报表时&#xff0c;选定一定的过滤条件&#xff0c;点击「查询」按钮将会通过 API 拉取相应的数据&#xff1b;前面说过&#xff0c;为了确保用户数据的安全性&#xff0c;Sugar BI上的所有数据请求都在Sugar BI的…...

学习vue(可与知乎合并)

一&#xff1a;组件及交互 1、什么是组件&#xff1f; 组件是可复用的 Vue 实例&#xff0c;且带有一个名字&#xff1a;在这个例子中是 。我们可以在一个通过 new Vue 创建的 Vue 根实例中&#xff0c;把这个组件作为自定义元素来使用&#xff1a; 声明组件 // 定义一个名…...

【UEFI实战】Linux下如何解析ACPI表

本文介绍如何在Linux下查看ACPI表示。使用的系统是Ubuntu18.04&#xff1a; Linux home 4.15.0-36-generic #39-Ubuntu SMP Mon Sep 24 16:19:09 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux 可以在如下的目录看到ACPI的基本信息&#xff1a; 但是默认的表都是不可以直接查看的&…...

Java-Redis持久化之RDB操作

Java-Redis持久化之RDB操作 1.为什么redis需要持久化&#xff1f;2.什么是RDB操作?3.请你用自己的话讲下RDB的过程?4.如何恢复rdb文件? 1.为什么redis需要持久化&#xff1f; Redis是内存数据库&#xff0c;如果不将内存数据库保存到磁盘&#xff0c;那么服务器进程退出&am…...

信号signal编程测试

信号会打断系统调用&#xff0c;慎用&#xff0c;就是用的时候测一测。 下面是信号的基础测试 信号 信号&#xff08;signal&#xff09;机制是UNIX系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。信号可以由各种异步事件产生&#xff0c;例如…...

Linux学习记录——이십삼 进程信号(2)

文章目录 1、可重入函数2、volatile关键字3、如何理解编译器的优化4、SIGCHLD信号 1、可重入函数 两个执行流都执行一个函数时&#xff0c;这个函数就被重入了。比如同一个函数insert&#xff0c;在main中执行时&#xff0c;这个进程时间片到了&#xff0c;嵌入了内核&#xf…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...