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…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...
