C++设计模式——Prototype Pattern原型模式
一,原型模式的定义
原型模式是一种创建型设计模式,它允许通过克隆已有对象来创建新对象,从而无需调用显式的实例化过程。
原型模式的设计,使得它可以创建一个与原型对象相同或类似的新对象,同时又可以减少对象实例化操作产生的性能开销,使得创建对象的操作更加便捷,它减少了大量不必要的重复工作,并提高了系统性能。
当创建对象的操作比较复杂和耗时的时候,原型模式则提供了一个更加高效和简单的创建对象的模式,它可以更加快速的创建对象的副本,且不需要依赖对象的某些实例化步骤,它避免了使用传统的new关键字创建对象实例时的复杂构造过程。
原型模式的主要缺点则是原型对象必须预先存在于系统中,并且需要预先进行注册。此时,如果有大量的原型对象需要被创建,并且每个原型对象都需要进行自定义,维护和管理这些原型对象可能会变得很复杂。
原型模式在现实生活中的抽象实例:
图形绘制:假设我们需要绘制不同形状的图形,可以定义一个图形类作为原型,然后通过克隆该原型对象来创建具体的图形对象。
陶艺制作:先制作一个陶艺原型作为参考,然后通过复制或克隆原型来制作出多个相似的陶艺品。
服装设计:设计师预先设计一套成衣样板,然后通过复制或克隆样板来制作出多件相似或不同款式的服装。

二,原型模式的结构
原型模式主要包含以下组件:
1.抽象原型(Prototype):定义克隆方法的接口,具体原型类通过实现这些方法来提供其自身的副本。
2.具体原型(ConcretePrototype):包含抽象原型类的具体实现,它会提供一个复制自身的方法,该方法将创建并返回一个对象副本。
3.客户端(Client):客户端使用原型类来创建新对象的副本,它会先获取原型对象,并使用原型对象的克隆方法来创建新的对象实例。
组件之间的工作步骤如下:
1.客户端通过实例化具体原型类,并调用其克隆方法来创建一个原型对象。
2.原型对象调用自身的克隆方法,将自身复制一份,返回一个克隆的对象。
3.客户端获取到克隆对象后,可以根据自身的业务需求对其进行进一步的修改和使用。
对应UML类图:

三,原型模式代码样例
Demo1:
#include <iostream>
#include <string>//定义原型基类
class Prototype {
public:virtual ~Prototype() {}virtual Prototype* clone() const = 0;virtual void print() const = 0;
};//定义具体原型类
class ConcretePrototype : public Prototype{
private:std::string name;
public:ConcretePrototype(const std::string& name) : name(name) {}Prototype* clone() const override {return new ConcretePrototype(*this);}void print() const override {std::cout << "Prototype: " << name << std::endl;}
};int main() {//创建原型对象ConcretePrototype prototype("Original");//使用原型对象创建新对象Prototype* clone = prototype.clone();clone->print();//释放内存delete clone;return 0;
}
运行结果:
Prototype: Original
Demo2:
#include <iostream>
#include <string>class Prototype {
public:virtual ~Prototype() {}virtual Prototype* clone() const = 0;virtual void info() const = 0;
};class ConcretePrototypeA: public Prototype {
public:ConcretePrototypeA(int id, std::string name): m_id(id), m_name(name) {}Prototype* clone() const override {return new ConcretePrototypeA(*this);}void info() const override {std::cout << "ConcretePrototypeA: id = "<< m_id << ", name = " << m_name << std::endl;}
private:int m_id;std::string m_name;
};class ConcretePrototypeB: public Prototype {
public:ConcretePrototypeB(std::string description): m_description(description) {}Prototype* clone() const override {return new ConcretePrototypeB(*this);}void info() const override {std::cout << "ConcretePrototypeB: description = " << m_description << std::endl;}
private:std::string m_description;
};int main() {ConcretePrototypeA* prototypeA = new ConcretePrototypeA(1, "First");Prototype* cloneA = prototypeA->clone();cloneA->info();ConcretePrototypeB* prototypeB = new ConcretePrototypeB("This is a prototype");Prototype* cloneB = prototypeB->clone();cloneB->info();delete prototypeA;delete cloneA;delete prototypeB;delete cloneB;return 0;
}
运行结果:
ConcretePrototypeA: id = 1, name = First
ConcretePrototypeB: description = This is a prototype
四,原型模式的应用场景
图形用户界面:创建可定制的控件,如Windows的对话框,设计一个原型控件,让用户根据需求选择属性进行定制。
文本编辑器开发:支持“剪切”、“复制”、“粘贴”的功能,使用已存在的文档作为复制和创建新文档的原型。
数据库开发:将数据库的某个状态视为原型,当需要创建新的数据库版本时,可以直接从这个原型复制。
配置工具开发:基于原型模式帮助快速生成配置对象,而无需每次都新建一个空的配置。
Web应用程序:让用户可以在原型基础上添加、修改字段,动态生成表单元素。
五,原型模式的优缺点
原型模式的优点:
简化了对象的创建过程,使得代码更加简洁且易于维护。
提高了动态创建对象和销毁对象的效率。
封装和隐藏了创建对象的细节。
减少了对构造函数的直接调用,提高了代码的性能。
支持灵活的定制具体对象的属性和方法。
原型模式的缺点:
需要实现克隆对象的接口。
需要配合深拷贝或浅拷贝来使用,可能会导致引用对象的复制。
有的原型模式基于递归的方式来克隆对象,可能会引起堆栈溢出的问题。
针对大型对象的复制,会占用特别多的内存。
六,代码实战
Demo1:基于智能指针封装的原型模式:
#include <memory>
#include <iostream>class Prototype {
public:virtual std::unique_ptr<Prototype> clone() const = 0;void printValue() const {std::cout << "Origin Value." << std::endl;}
};class ConcretePrototype : public Prototype {
private:int value;
public:ConcretePrototype(int v) : value(v) {}std::unique_ptr<Prototype> clone() const override {return std::make_unique<ConcretePrototype>(value);}void printValue() const {std::cout << "Value: " << value << std::endl;}
};int main() {auto prototype = std::make_unique<ConcretePrototype>(5);auto clonedPrototype = prototype->clone();prototype->printValue();clonedPrototype->printValue();return 0;
}
运行结果:
Value: 5
Origin Value.
Demo2:基于工厂的方式管理各种原型
#include <iostream>
#include <string>
#include <map>class Prototype {
public:int data;std::string name;Prototype(int data, const std::string& name) : data(data), name(name) {}virtual Prototype* clone() {return new Prototype(*this);}
};class PrototypeFactory {
private:std::map<std::string, Prototype*> prototypes;
public:PrototypeFactory() {prototypes["original"] = new Prototype(40, "Original");prototypes["copy"] = new Prototype(100, "Copy");}Prototype* create(const std::string& type) {return prototypes[type]->clone();}
};int main() {PrototypeFactory factory;Prototype* original = factory.create("original");Prototype* copy = factory.create("copy");std::cout << "Original: data="<< original->data<< ", name="<< original->name << std::endl;std::cout << "Copy: data="<< copy->data<< ", name="<< copy->name << std::endl;delete original;delete copy;return 0;
}
运行结果:
Original: data=40, name=Original
Copy: data=100, name=Copy
七,参考阅读
https://softwarepatterns.com/cpp/prototype-software-pattern-cpp-example
https://www.geeksforgeeks.org/prototype-design-pattern/
https://www.tutorialspoint.com/design_pattern/prototype_pattern.html
https://sourcemaking.com/design_patterns/prototype
https://softwareparticles.com/design-patterns-prototype/
相关文章:
C++设计模式——Prototype Pattern原型模式
一,原型模式的定义 原型模式是一种创建型设计模式,它允许通过克隆已有对象来创建新对象,从而无需调用显式的实例化过程。 原型模式的设计,使得它可以创建一个与原型对象相同或类似的新对象,同时又可以减少对象实例化…...
Vue3 : ref 与 reactive
目录 一.ref 二.reactive 三.ref与reactive的区别 四.总结 一.ref 在 Vue 3 中,ref 是一个用于创建可读写且支持数据跟踪的响应式引用对象。它主要用于在组件内部创建响应式数据,这些数据可以是基本类型(如 number、string、boolean&…...
html实现好看的多种风格手风琴折叠菜单效果合集(附源码)
文章目录 1.设计来源1.1 风格1 -图文结合手风琴1.2 风格2 - 纯图片手风琴1.3 风格3 - 导航手风琴1.4 风格4 - 双图手风琴1.5 风格5 - 综合手风琴1.6 风格6 - 简描手风琴1.7 风格7 - 功能手风琴1.8 风格8 - 全屏手风琴1.9 风格9 - 全屏灵活手风琴 2.效果和源码2.1 动态效果2.2 源…...
Nacos分布式配置中心
分布式配置的优势: 不需要重新发布我们的应用 新建父工程:【将它作为跟 所以要把父工程里面的src删掉】 新建子模块: 新建bootstrap.properties: 在使用Nacos作为配置中心时,推荐在bootstrap.properties中配置Nacos相…...
C# WinForm 中 DataGridView 实现单元格cell 能进编辑状态但是不能修改单元格的效果
在Windows Forms(WinForms)开发中,DataGridView 控件是一个功能强大的组件, 用于显示和管理表格数据。无论是展示大量数据,还是实现交互式的数据操作, DataGridView 都能提供多样的功能支持,比如…...
GANs-生成对抗网络
参考: https://mp.weixin.qq.com/s?__bizMjM5ODIwNjEzNQ&mid2649887403&idx3&snf61fc0e238ffbc56a7f1249b93c20690&chksmbfa0f632460e035f00be6cc6eb09637d91614e4c31da9ff47077ca468caad1ee27d08c04ca32&scene27 https://cloud.tencent.com…...
e冒泡排序---复杂度O(X^2)
排序原理: 1.比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置。 2.对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值, public class 冒泡排序 {public static void main(String[] args) {I…...
C语言--结构体(学习笔记)
内容借鉴于b站杜远超官方频道(C语言结构体详解【干货】) 首先C语言中定义变量格式为“数据类型 变量名”,如int a; float b;等等。 那么结构体则是将多个变量(数据类型 变量名)结合在一起的一种新的数据类型&…...
Vue项目中实现用户登录后跳回原地址
本地存储 在 Vue 3 中,你可以使用 Vue Router 和 sessionStorage 或 localStorage 来实现用户登录后跳回原来的页面。以下是一种常见的实现方式: 在用户登录之前,记录当前页面的路由路径: 在需要登录的页面组件中,在…...
【Google Chrome Windows 64 version及 WebDriver 版本】
最近升级到最新版本Chrome后发现页面居然显示错乱实在无语, 打算退回原来的版本, 又发现官方只提供最新的版本下载, 为了解决这个问题所有收集了Chrome历史版本的下载地址分享给大家. Google Chrome Windows version 64 位 VersionSize下载地址Date104.0.5112.10282.76 MBhtt…...
[ffmpeg] 音视频编码
本文主要梳理 ffmpeg 中音视频编码的常用函数 API调用 常用 API const AVCodec *avcodec_find_encoder(enum AVCodecID id); AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); void avcodec_free_context(AVCodecContext **avctx); int avcodec_open2(AVCode…...
springboot+redis+缓存
整合 添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 连接redis,配置yml文件 主机 端口号 数据库是哪一个 密码 配置类 p…...
关于http的206状态码和416状态码的意义、断点续传以及CORS使用Access-Control-Allow-Origin来允许跨域请求
一、关于http的206状态码和416状态码的意义及断点续传 HTTP 2xx范围内的状态码表明客户端发送的请求已经被服务器接受并且被成功处理了,HTTP/1.1 206状态码表示客户端通过发送范围请求头Range抓取到了资源的部分数据,一般用来解决大文件下载问题,一般CDN…...
SOMEIP_ETS_114: SD_Entries_Length_wrong_combined
测试目的: 验证DUT能够拒绝一个包含两个正确条目但条目数组长度不正确的SubscribeEventgroup消息,并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议,当接收到一个条目数组长度与实际条目数量不匹配的Sub…...
SQL:DATEDIFF函数
DATEDIFF函数是用于计算两个日期之间的时间间隔的函数,它在不同的编程语言和数据库系统中都有广泛的应用。以下是对DATEDIFF函数的详细解析: 一、函数用途 DATEDIFF函数的主要用途是计算两个日期之间的时间间隔,这个间隔可以是年、季度、月…...
MATLAB 可视化基础:绘图命令与应用
目录 1. 绘制子图1.1基本绘图命令1.2. 使用 subplot 函数1.3. 绘图类型 2.MATLAB 可视化进阶(以下代码均居于以上代码的数据定义上实现)2.1. 极坐标图2.3. 隐函数的绘制 3.总结 在数据分析和科学计算中,数据可视化是理解和解释结果的关键工具。今天,我将…...
掌握 Python 异常处理的实战技巧:从基础到高级应用20240918
掌握 Python 异常处理的实战技巧:从基础到高级应用 引言 在 Python 编程中,异常处理是保障代码稳健性和可靠性的关键要素之一。无论是在网络请求、资源访问,还是复杂的业务逻辑中,异常处理都不可或缺。本文将从 Python 异常的基…...
One API 部署与配置指南
技术文档:One API 部署与配置指南 概述 One API 是一个多功能的 API 管理平台,支持自定义设置、用户管理、多种登录注册方式、主题切换等。本文档提供了详细的部署和配置指南,帮助用户快速搭建和使用 One API。 部署 基于 Docker 部署 D…...
国密起步7:BouncyCastle使用SM4自定义格式加解密C#版
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 github源码指引的指引-CSDN博…...
Qt优秀开源项目之二十三:QSimpleUpdater
QSimpleUpdater是开源的自动升级模块,用于检测、下载和安装更新。 github地址:https://github.com/alex-spataru/QSimpleUpdater QSimpleUpdater目前Star不多(911个),但已在很多开源项目看到其身影,比如Not…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
