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

设计模式——原型模式(创建型)

引言

原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

问题

如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢? 首先, 你必须新建一个属于相同类的对象。 然后, 你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。

不错! 但有个小问题。 并非所有对象都能通过这种方式进行复制, 因为有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的。

直接复制还有另外一个问题。 因为你必须知道对象所属的类才能创建复制品, 所以代码必须依赖该类。 即使你可以接受额外的依赖性, 那还有另外一个问题: 有时你只知道对象所实现的接口, 而不知道其所属的具体类, 比如可向方法的某个参数传入实现了某个接口的任何对象。

解决方案

原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个 克隆方法。

所有的类对 克隆方法的实现都非常相似。 该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。 你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。

支持克隆的对象即为原型。 当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造。

 

 其运作方式如下: 创建一系列不同类型的对象并不同的方式对其进行配置。 如果所需对象与预先配置的对象相同, 那么你只需克隆原型即可, 无需新建一个对象。

真实世界类比

现实生活中, 产品在得到大规模生产前会使用原型进行各种测试。 但在这种情况下, 原型只是一种被动的工具, 不参与任何真正的生产活动。

 由于工业原型并不是真正意义上的自我复制, 因此细胞有丝分裂 (还记得生物学知识吗?) 或许是更恰当的类比。 有丝分裂会产生一对完全相同的细胞。 原始细胞就是一个原型, 它在复制体的生成过程中起到了推动作用。

原型模式结构

 

伪代码

在本例中, 原型模式能让你生成完全相同的几何对象副本, 同时无需代码与对象所属类耦合。

 所有形状类都遵循同一个提供克隆方法的接口。 在复制自身成员变量值到结果对象前, 子类可调用其父类的克隆方法。

// 基础原型。
abstract class Shape isfield X: intfield Y: intfield color: string// 常规构造函数。constructor Shape() is// ……// 原型构造函数。使用已有对象的数值来初始化一个新对象。constructor Shape(source: Shape) isthis()this.X = source.Xthis.Y = source.Ythis.color = source.color// clone(克隆)操作会返回一个形状子类。abstract method clone():Shape// 具体原型。克隆方法会创建一个新对象并将其传递给构造函数。直到构造函数运
// 行完成前,它都拥有指向新克隆对象的引用。因此,任何人都无法访问未完全生
// 成的克隆对象。这可以保持克隆结果的一致。
class Rectangle extends Shape isfield width: intfield height: intconstructor Rectangle(source: Rectangle) is// 需要调用父构造函数来复制父类中定义的私有成员变量。super(source)this.width = source.widththis.height = source.heightmethod clone():Shape isreturn new Rectangle(this)class Circle extends Shape isfield radius: intconstructor Circle(source: Circle) issuper(source)this.radius = source.radiusmethod clone():Shape isreturn new Circle(this)// 客户端代码中的某个位置。
class Application isfield shapes: array of Shapeconstructor Application() isCircle circle = new Circle()circle.X = 10circle.Y = 10circle.radius = 20shapes.add(circle)Circle anotherCircle = circle.clone()shapes.add(anotherCircle)// 变量 `anotherCircle(另一个圆)`与 `circle(圆)`对象的内// 容完全一样。Rectangle rectangle = new Rectangle()rectangle.width = 10rectangle.height = 20shapes.add(rectangle)method businessLogic() is// 原型是很强大的东西,因为它能在不知晓对象类型的情况下生成一个与// 其完全相同的复制品。Array shapesCopy = new Array of Shapes.// 例如,我们不知晓形状数组中元素的具体类型,只知道它们都是形状。// 但在多态机制的帮助下,当我们在某个形状上调用 `clone(克隆)`// 方法时,程序会检查其所属的类并调用其中所定义的克隆方法。这样,// 我们将获得一个正确的复制品,而不是一组简单的形状对象。foreach (s in shapes) doshapesCopy.add(s.clone())// `shapesCopy(形状副本)`数组中包含 `shape(形状)`数组所有// 子元素的复制品。

原型模式适合应用场景

 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。

 这一点考量通常出现在代码需要处理第三方代码通过接口传递过来的对象时。 即使不考虑代码耦合的情况, 你的代码也不能依赖这些对象所属的具体类, 因为你不知道它们的具体信息。

原型模式为客户端代码提供一个通用接口, 客户端代码可通过这一接口与所有实现了克隆的对象进行交互, 它也使得客户端代码与其所克隆的对象具体类独立开来。

 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。

 在原型模式中, 你可以使用一系列预生成的、 各种类型的对象作为原型。

客户端不必根据需求对子类进行实例化, 只需找到合适的原型并对其进行克隆即可。

 实现方式

  1. 创建原型接口, 并在其中声明 克隆方法。 如果你已有类层次结构, 则只需在其所有类中添加该方法即可。

  2. 原型类必须另行定义一个以该类对象为参数的构造函数。 构造函数必须复制参数对象中的所有成员变量值到新建实体中。 如果你需要修改子类, 则必须调用父类构造函数, 让父类复制其私有成员变量值。

    如果编程语言不支持方法重载, 那么你可能需要定义一个特殊方法来复制对象数据。 在构造函数中进行此类处理比较方便, 因为它在调用 new运算符后会马上返回结果对象。

  3. 克隆方法通常只有一行代码: 使用 new运算符调用原型版本的构造函数。 注意, 每个类都必须显式重写克隆方法并使用自身类名调用 new运算符。 否则, 克隆方法可能会生成父类的对象。

  4. 你还可以创建一个中心化原型注册表, 用于存储常用原型。

    你可以新建一个工厂类来实现注册表, 或者在原型基类中添加一个获取原型的静态方法。 该方法必须能够根据客户端代码设定的条件进行搜索。 搜索条件可以是简单的字符串, 或者是一组复杂的搜索参数。 找到合适的原型后, 注册表应对原型进行克隆, 并将复制生成的对象返回给客户端。

    最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。

原型模式优缺点

  •  你可以克隆对象, 而无需与它们所属的具体类相耦合。
  •  你可以克隆预生成原型, 避免反复运行初始化代码。
  •  你可以更方便地生成复杂对象。
  •  你可以用继承以外的方式来处理复杂对象的不同配置。

与其他模式的关系

  • 在许多设计工作的初期都会使用工厂方法模式 (较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂模式、原型模式或生成器模式(更灵活但更加复杂)。
  • 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
  • 原型可用于保存命令模式的历史记录。
  • 大量使用组合模式和装饰模式的设计通常可从对于原型的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造。
  • 原型并不基于继承,因此没有继承的缺点。另一方面,原型需要对被复制对象进行复杂的初始化。工厂方法基于继承,但是它不需要初始化步骤。
  • 有时候原型可以作为备忘录模式的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便地重建。
  • 抽象工厂、生成器和原型都可以用单例模式来实现。

相关文章:

设计模式——原型模式(创建型)

引言 原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。 问题 如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢? 首先, 你必须新建一个属于…...

深眸科技以机器视觉高性能优势,为消费电子行业提供优质解决方案

机器视觉技术近年来发展迅速,基于计算机对图像的处理与分析,能够识别和辨别目标物体,被广泛应用于人工智能、智能制造等领域。 机器视觉凭借着高精度、高效率、灵活性和可靠性等优势,不断推进工业企业生产自动化和智能化进程&…...

React setState()的两种书写方法对比

在React中,setState()方法是一个非常重要的函数,用于更新组件的状态。它有两种常见的书写方式:对象解构赋值和使用函数。本文将对比这两种方法,并解释它们的优缺点和适用场景。 首先,让我们来看看对象解构赋值这种方法…...

orb-slam2学习总结

目录 视觉SLAM 1、地图初始化 2、ORB_SLAM地图初始化流程 3、ORB特征提取及匹配 1、对极几何 2、对极约束 (epipolar constraint) 3、基础矩阵F、本质矩阵E 5、单目尺度不确定性 6、单应矩阵(Homography Matrix) 6.1 什么是单应矩…...

通过wireshark判断web漏洞的流量特征

sql注入 定位http协议,通过查找get请求定位到关键字 抓到关键字union select xss 定位get请求的关键字 文件上传 找到对应的上传数据包,追踪tcp流 文件包含、文件读取 查看url找到包含的关键字 在根路径写入一个phpinfo(); 使用../进行目录遍…...

Command ‘npm‘ not found, but can be installed with:sudo apt install npm 解决方案

问题描述 今天在执行 npm install -g npx 报错 Command npm not found, but can be installed with: sudo apt install npm 解决方案 sudo apt-get remove npm sudo apt-get remove nodejs-legacy sudo apt-get remove nodejs sudo rm /usr/bin/node sudo apt-get install …...

【Hadoop_04】HDFS的API操作与读写流程

1、HDFS的API操作1.1 客户端环境准备1.2 API创建文件夹1.3 API上传1.4 API参数的优先级1.5 API文件夹下载1.6 API文件删除1.7 API文件更名和移动1.8 API文件详情和查看1.9 API文件和文件夹判断 2、HDFS的读写流程(面试重点)2.1 HDFS写数据流程2.2 网络拓…...

go-zero开发入门之网关往rpc服务传递数据

go-zero 的网关往 rpc 服务传递数据时,可以使用 headers,但需要注意前缀规则,否则会发现数据传递不过去,或者对方取不到数据。 go-zero 的网关对服务的调用使用了第三方库 grpcurl,入口函数为 InvokeRPC: …...

Word插件-好用的插件-批量插入图片-大珩助手

现有100张图片,需要批量插入word中,并在word中以每页6张图片的形式呈现,请问怎样做? 使用word大珩助手,多媒体-插入图片,根据图片的长宽,选择连续图片、一行2个图或一行3个图,可一次…...

小程序域名SSL证书能用免费的吗?

众所周知,目前小程序要求域名强制使用https协议,否则无法上线。但是对于大多数开发者来说,为每一个小程序都使用上付费的SSL证书,也是一笔不小的支出。那么小程序能使用免费的SSL证书吗? 答案是肯定的。目前市面上可选…...

selenium自动化(中)

显式等待与隐式等待 简介 在实际工作中等待机制可以保证代码的稳定性,保证代码不会受网速、电脑性能等条件的约束。 等待就是当运行代码时,如果页面的渲染速度跟不上代码的运行速度,就需要人为的去限制代码执行的速度。 在做 Web 自动化时…...

uniapp app将base64保存到相册,uniapp app将文件流保存到相册

如果是文件流可以先转base64详情见>uniapp 显示文件流图片-CSDN博客 onDown(){let base64 this.qrcodeUrl ; // base64地址const bitmap new plus.nativeObj.Bitmap("test");bitmap.loadBase64Data(base64, function() {const url "_doc/" new Dat…...

Navicat 技术指引 | 适用于 GaussDB 分布式的服务器对象的创建/设计

Navicat Premium(16.3.3 Windows版或以上)正式支持 GaussDB 分布式数据库。GaussDB分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能,还提供强大的高阶功能(如模型、结构…...

五、HotSpot细节实现

一、并发标记与三色标记 问题:三色标记到底发生在什么阶段,替代了什么。并发标记 1、并发标记( Concurrent Marking) 从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗…...

DRBD分布式存储实验

DRBD DRBD的全称为:Distributed Replicated Block Device (DRBD) 分布式块设备复制 与心跳连接结合使用,构建高可用性(HA)的集群。 实现方式是通过网络来镜像(mirror)整个设备。它允许用户在远程机器上建立一个本地块设备的实时镜像。DRBD负责接收数据…...

go的结构体作为返回值

结构体有两种方式作为返回值 结构体结构体指针 代码 package mainimport ("fmt" )type SS struct {Name stringAge int }func getInfo() (*SS) {var ac SS{}ac.Age 1return &ac }func getInfo1() (aa *SS) {aa.Age 1return }func getInfo2() (SS) {var ac…...

uniapp的subnvue苹果适配(ios)谷歌地图问题

谷歌地图,google地图,调整宽度。这个适配花了点时间,苹果IOS宽度一直无效失灵,赶紧记录分享,很坑。可能所有的ios的subnvue适配都这样。看了网上很多方法无效,最终找到试出答案。 pages.json的配置宽度无效…...

项目实战之RabbitMQ重试机制进行消息补偿通知

🧑‍💻作者名称:DaenCode 🎤作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。 😎人生感悟:尝尽人生百味,方知世间冷暖。 文章目录 🌟架构图&#x…...

MySQL之数据库的创建指令

创建数据库 #创建数据库指令: CREATE DATABASE hsp_db1 #创建名字为关键字的数据库,为规避关键字,可以使用反引号 CREATE DATABASE CREATE#删除数据库指令: DROP DATABASE hsp_db1 DROP DATABASE CREATE如果不指定在这里插入代码片…...

[网络安全]批处理(脚本)编写

Windows DOS命令Linux 一.作用: 自上而下成批次处理每一条命令,直到执行到最后一条 二.如何创建批处理: 扩展名:.bat创建办法:新建一个记事本,把扩展名改为 .bat 三.编辑方法: 右击 -编辑 1).一行一个命令 四.批处理命令: pause 暂停 (及时后面有命令,也不执行)echo …...

终极指南:如何利用Storybook实现集中式日志组件管理

终极指南:如何利用Storybook实现集中式日志组件管理 【免费下载链接】storybook Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation 项目地址: https://gitcode.com/GitHub_Trending/st/storybook…...

LinkSwift:基于JavaScript的网盘直链解析工具技术解析与应用指南

LinkSwift:基于JavaScript的网盘直链解析工具技术解析与应用指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云…...

Vue甘特图实战:从零构建高效项目管理视图

1. 为什么选择VueECharts实现甘特图 在项目管理工具中,甘特图是最直观的任务排期展示方式。传统方案往往需要引入复杂的第三方库,而VueECharts的组合却能以最小成本实现专业效果。我去年负责一个电商大促项目时,就用这个方案替代了原本采购的…...

3dsconv完全指南:从格式转换到批量处理的开源解决方案

3dsconv完全指南:从格式转换到批量处理的开源解决方案 【免费下载链接】3dsconv Python script to convert Nintendo 3DS CCI (".cci", ".3ds") files to the CIA format 项目地址: https://gitcode.com/gh_mirrors/3d/3dsconv 你是否曾…...

如何解决多显示器壁纸管理的三大痛点:Superpaper跨平台解决方案实战指南

如何解决多显示器壁纸管理的三大痛点:Superpaper跨平台解决方案实战指南 【免费下载链接】superpaper A cross-platform multi monitor wallpaper manager. 项目地址: https://gitcode.com/gh_mirrors/su/superpaper 在多显示器工作环境中,你是否…...

数据主权守护者:解决微信聊天记录永久保存难题的开源方案

数据主权守护者:解决微信聊天记录永久保存难题的开源方案 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/We…...

避坑指南:Vue2中xlsx-style设置行高无效?手把手教你修改源码并封装通用导出函数

Vue2中xlsx-style行高设置失效的深度解决方案与工程化封装 在Vue2项目中处理Excel导出时,很多开发者会遇到一个令人困惑的问题:明明按照xlsx-style的文档设置了row.hpx属性,导出的Excel文件却依然保持默认行高。这背后其实隐藏着xlsx.js源码中…...

实战演练:如何利用SQLMap快速检测银行储物柜管理系统的CVE-2023-0562漏洞

实战指南:SQLMap在银行储物柜管理系统漏洞检测中的高效应用 银行储物柜管理系统作为金融机构关键基础设施,其安全性直接关系到客户资产安全。近年来曝光的CVE-2023-0562漏洞再次提醒我们,即使是基础防护措施也可能存在致命缺陷。本文将手把手…...

潮玩盲盒小程序开发踩坑?这些解法要记住

潮玩盲盒小程序开发踩坑?这些解法要记住✅ 哈喽~做潮玩盲盒小程序开发,很多伙伴都会踩坑!今天整理了高频开发痛点对应应对方法,新手也能避坑👇 🎮 开发痛点应对方案(一痛一解&#x…...

全自动洗衣机组态王与三菱PLC联机及仿真探索

全自动洗衣机组态王6.53,6.60和三菱PLC联机和仿真程序包最近在研究自动化控制领域相关内容,接触到了全自动洗衣机组态王 6.53、6.60 与三菱 PLC 的联机以及仿真程序包,感觉很有意思,今天就来和大家分享分享。 一、组态王与三菱 PLC 联机的意义…...