C++ 【原型模式】

简单介绍

原型模式是一种创建型设计模式 | 它使你能够复制已有对象,客户端不需要知道要复制的对象是哪个类的实例,只需通过原型工厂获取该对象的副本。 以后需要更改具体的类或添加新的原型类,客户端代码无需改变,只需修改原型工厂即可 。

基础理解

Q:为什么使用原型模式
A:如果你有一个对象, 并希望生成与其完全相同的一个复制品。如果直接复制:

  1. 对方可能有私有成员变量(你无法访问私有)
  2. 不知道对方的具体类(可能使用父类接口,但我们需要复制的是具体子类)

解决方案

  • 那我们在外部无法克隆,便可以想想在类的内部设置一个通用的克隆接口。对象可以访问同类对象的私有
  • 克隆返回的对象的配置要与预先的配置相同。甚至有时候当构造函数变量很多几十个,克隆可以完全代替子类构造函数

UML 图

原型注册表 (Prototype Registry) 最简单的注册表原型是一个 名称 → 原型的哈希表。
在这里插入图片描述

实现步骤

  1. 创建原型接口, 并在其中声明 克隆方法。 如果你已有类层次结构, 则只需在其所有类中添加该方法即可。
  2. 原型类必须另行定义一个以该类对象为参数的构造函数。如果你需要修改子类,贼需要调用父类构造函数,让父类复制变量与子类保持一致。
  3. 克隆方法通常只有一行代码newConcretePrototype1(*this);每个类都必须显式重写克隆方法并使用自身类名调用 new运算符。
  4. 还可以创建一个原型注册表, 用于存储常用原型。将对子类构造函数的直接调用替换为对原型注册表的调用。
#include <iostream>
#include <string>
#include <unordered_map>using std::string;enum Type //枚举类
{PROTOTYPE_1 = 0,PROTOTYPE_2
};
//抽象原型类
class Prototype
{
protected:string prototype_name_;float prototype_field_;public:Prototype() {}Prototype(string prototype_name): prototype_name_(prototype_name){}virtual ~Prototype() {}virtual Prototype *Clone() const = 0;virtual void Method(float prototype_field){this->prototype_field_ = prototype_field;std::cout << "从 " << prototype_name_ << " 中调用 Method 方法,字段值为:" << prototype_field << std::endl;}
};
//具体原型类1
class ConcretePrototype1 : public Prototype
{
private:float concrete_prototype_field1_;public:ConcretePrototype1(string prototype_name, float concrete_prototype_field): Prototype(prototype_name), concrete_prototype_field1_(concrete_prototype_field){}Prototype *Clone() const override{return new ConcretePrototype1(*this);}
};
//具体原型类2
class ConcretePrototype2 : public Prototype
{
private:float concrete_prototype_field2_;public:ConcretePrototype2(string prototype_name, float concrete_prototype_field): Prototype(prototype_name), concrete_prototype_field2_(concrete_prototype_field){}Prototype *Clone() const override{return new ConcretePrototype2(*this);}
};
//原型注册表
class PrototypeFactory
{
private:std::unordered_map<Type, Prototype *, std::hash<int>> prototypes_;public:PrototypeFactory(){prototypes_[Type::PROTOTYPE_1] = new ConcretePrototype1("原型 1", 50.f);prototypes_[Type::PROTOTYPE_2] = new ConcretePrototype2("原型 2", 60.f);}~PrototypeFactory(){for (auto it = prototypes_.begin(); it != prototypes_.end(); ++it){delete it->second;}prototypes_.clear();}Prototype *CreatePrototype(Type type){return prototypes_[type]->Clone();}
};void Client(PrototypeFactory &prototype_factory)
{std::cout << "创建原型 1\n";Prototype *prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_1);prototype->Method(90);delete prototype;std::cout << "\n";std::cout << "创建原型 2\n";prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_2);prototype->Method(10);delete prototype;
}int main()
{PrototypeFactory *prototype_factory = new PrototypeFactory();Client(*prototype_factory);delete prototype_factory;return 0;
}

应用场景

你需要复制一些对象, 且独立于这些对象所属的具体类,减少耦合

通常出现在代码需要处理第三方代码通过接口传递过来的对象时。 即使不考虑代码耦合的情况, 你的代码也不能依赖这些对象所属的具体类: 可能人家更改了一下,你就崩了。因为你的客户端也需要更改。如我开头所说的一样

子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。

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

与其他模式的关系

  • 在许多设计工作的初期都会使用简单工厂模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式 (更灵活但更加复杂)。

  • 抽象工厂模式通常基于一组简单工厂, 但你也可以使用原型模式来生成这些类的方法。(在工厂类中添加clone 方法,动态地创建具体的工厂类,而不需要使用new 创建)

  • 原型可用于保存命令模式的历史记录。保存历史记录,可以在需要时重新执行或撤销先前执行的命令。

  • 大量使用组合模式和装饰模式的设计通常可从对于原型的使用中获益。 你可以通过该模式来复制复杂结构, 而非从零开始重新构造。

  • 原型并不基于继承, 因此没有继承的缺点。 另一方面, 原型需要对被复制对象进行复杂的初始化。 简单工厂基于继承, 但是它不需要初始化步骤。

  • 有时候原型可以作为备忘录模式的一个简化版本。当对象的状态相对简单,且不需要频繁保存和恢复时,原型模式是一个更简洁的方案。

代码示例

优缺点

优点缺点
你可以克隆对象, 而无需与它们所属的具体类相耦合克隆包含循环引用的复杂对象可能会非常麻烦。
你可以克隆预生成原型, 避免反复运行初始化代码。
你可以更方便地生成复杂对象。
你可以用继承以外的方式来处理复杂对象的不同配置。

如果有错还望指正。有什么建议也可以留言。
你的赞是我的莫大动力。谢谢大家。

参考文档

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

各类系统业务功能架构图整理

一、前言 很多软件系统一直经久不衰&#xff0c;主要这些系统都是一些生产工作经营不可或缺的系统。比如财务系统&#xff0c;商城系统&#xff0c;支付系统&#xff0c;供应链系统&#xff0c;人力资源管理系统&#xff0c;ERP系统等等。这些系统不管大公司还是小公司往往都需…

简约轻量-失信录系统源码

失信录系统-最新骗子收录查询系统源码 首页查询&#xff1a; 举报收录页&#xff1a; 后台管理页&#xff1a; 失信录系统 V1.0.0 更新内容&#xff1a; 1.用户查询,举报功能 2.界面独立开发 3.拥有后台管理功能 4.xss,sql安全过滤 5.平台用户查询 6.用户中心&#xff08;待完…

揭开“栈和队列”的神秘面纱

前言 在线性表中不止有顺序表和链表&#xff0c;今天的主角就如标题所说--->认识栈和队列。把他们俩放一起总结是有原因的&#xff0c;还请看官听我娓娓道来~ 什么是栈&#xff1f; 栈&#xff08;stack&#xff09;是限定仅在表尾进行插入和删除操作的线性表 咱可以把栈理…

【活动创作】未来AI技术方面会有哪些创业机会

放假期间突然看到这个活动创作&#xff0c;觉得很有意思&#xff0c;既然如此&#xff0c;我就先让AI来回答一下吧&#xff0c;哈哈 1、文心一言 首先来看看文心一言的回答&#xff1a; 2、讯飞星火 然后来看看讯飞星火的回答&#xff1a; 3、个人感受 最后来说说给人感受吧&am…

深入剖析主机安全中的零信任机制及其实施原理

引言 在数字化转型加速与云端服务普及的大背景下&#xff0c;传统依赖边界的网络安全模式逐渐显露出其局限性。面对愈发复杂多变的威胁环境&#xff0c;零信任安全架构作为新一代的安全范式应运而生&#xff0c;尤其是在主机层面的安全实践中&#xff0c;零信任机制正扮演着至…

【QT+QGIS跨平台编译】056:【pdal_arbiter+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、pdal_arbiter介绍二、pdal下载三、文件分析四、pro文件五、编译实践一、pdal_arbiter介绍 pdal_arbiter是 PDAL 项目的一个库,用于帮助管理应用程序运行在 EC2 实例上的 AWS 凭证。 当应用程序需要调用 AWS API 时,它们必须使用 AWS 凭据对 AP…

达梦DMHS-Manager工具日常操作

目录 1、前言 2、同步服务管理 2.1、DMHS Agent节点管理 2.2、DMHS实例节点管理 2.3、DMHS模块节点管理 3、监控及告警 3.1、主机资源监控 3.2、同步链路监控 3.3、告警配置 4、系统管理 4.1、用户管理 4.2、角色管理 4.3、系统配置 4.4、审计信息 5、联机帮助 …

2024软件测试全套教程,软件测试自学线路图

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

4.7Qt

自由发挥应用场景实现一个登录窗口界面。 mywidget.cpp #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口相关设置this->setWindowTitle("原神启动");this->setWindowIcon(QIcon("C:\\Users\\17212\\Pict…

工具推荐-针对Nacos利器-NacosExploitGUI_v4.0

Nacos是由阿里所开发的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 工具简介 集成Nacos的各种poc Nacos控制台默认口令漏洞(nacos,nacos)Nacostoken.secret.key默认配置(QVD-2023-6271)Nacos-clientYaml反序列化漏洞Nacos Jraft Hessian反序列化漏洞…

Redis中的Sentinel(六)

Sentinel 选举领头Sentinel. 当一个主服务器被判断为客观下线时&#xff0c;监视这个下线主服务器的各个Sentinel会进行协商&#xff0c;选举出一个领头Sentinel,并由领头 Sentinel对下线主服务器执行故障转移操作。以下是Redis选举领头Sentinel的规则和方法: 1.所有在线的S…

AcWing---转圈游戏---快速幂

太久没写快速幂了... 这是一道数学题orz&#xff0c;能看出来的话答案就是 &#xff0c;但是很大&#xff0c;同时还要mod n&#xff0c;直接用快速幂即可。 快速幂模版&#xff1a; long long int power(long long int a,long long int b,long long int mod){long long int r…

设计模式之命令模式(上)

命令模式 1&#xff09;概述 1.定义 命令模式(Command Pattern) 将一个请求封装为一个对象&#xff0c;可以用不同的请求对客户进行参数化&#xff1b;对请求排队或者记录请求日志&#xff0c;以及支持可撤销的操作。 2.作用 命令模式可以将请求发送者和接收者完全解耦&am…

31.2k star, 免费开源的白板绘图工具 tldraw

31.2k star, 免费开源的白板绘图工具 tldraw 分类 开源分享 项目名: tldraw -- 无限画布白板 Github 开源地址&#xff1a; https://github.com/tldraw/tldraw 在线测试地址&#xff1a; tldraw 文档地址&#xff1a; tldraw SDK tldraw 是一款开源免费的无限画布白板&…

基于springboot实现社区医院信息平台系统项目【项目源码+论文说明】

基于springboot实现社区医院信息平台系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了社区医院信息平台的开发全过程。通过分析社区医院信息平台管理的不足&#xff0c;创建了一个计算机管理社区医院信…

【Linux】环境基础开发工具使用——vim使用

Linux 软件包管理器 yum 什么是软件包 1.在 Linux 下安装软件 , 一个通常的办法是下载到程序的源代码 , 并进行编译 , 得到可执行程序 . 2.但是这样太麻烦了 , 于是有些人把一些常用的软件提前编译好 , 做成软件包 ( 可以理解成 windows 上的安装程序) 放在一个服务器…

人工智能 - 服务于谁?

人工智能服务于谁&#xff1f; 人工智能服务于生存&#xff0c;其最终就是服务于战争&#xff08;热战、技术战、经济战&#xff09; 反正就是为了活着而战的决策。 既然人工智能所有结果&#xff0c;来自大数据的分挖掘&#xff08;分析&#xff09;也就是数据的应用&#x…

Qt实现Kermit协议(四)

3 实现 3.3 KermitRecvFile 该模块实现了Kermit接收文件功能。 序列图如下&#xff1a; 3.3.1 KermitRecvFile定义 class QSerialPort; class KermitRecvFile : public QObject, public Kermit {Q_OBJECT public:explicit KermitRecvFile(QSerialPort *serial, QObject *…

JSON在线工具使用文档

功能支持 ctrls json格式化游览器本地保存ctrla ctrlc 自动检测选中范围是否是全选&#xff0c;然后按照格式化方式添加到粘贴板中json 粘贴JSON自动格式化json可视化修改json压缩复制json层级折叠json关键key 搜索(自动提示高亮)满足某些近视的可以自行调整字体大小, 并且会游…

鸿蒙内核源码分析 (内存管理篇) | 虚拟内存全景图是怎样的

初始化整个内存 OsSysMemInitOsMainmain从 main() 跟踪可看内存部分初始化是在 OsSysMemInit() 中完成的。 UINT32 OsSysMemInit(VOID) {STATUS_T ret;OsKSpaceInit();//内核空间初始化ret OsKHeapInit(OS_KHEAP_BLOCK_SIZE);// 内核动态内存初始化 512K if (ret ! LOS_OK…