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.mzph.cn/news/796985.shtml

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

相关文章

[ 云计算 | AWS ] ChatGPT 竞争对手 Claude 3 上线亚马逊云,实测表现超预期

文章目录 一、前言二、Claude 3 介绍以及相关测试细节三、在亚马逊云科技上体验 Claude 33.1 在 Amazon Bedrock 服务中配置 Claude 33.2 为聊天配置使用 Claude 3 模型3.3 Caude 3 Sonet 聊天体验 四、文末总结五、参考文献 一、前言 3月4号&#xff0c;Anthropic 发布了号称…

第十四届蓝桥杯C/C++大学B组题解(一)

1、日期统计 #include <bits/stdc.h> using namespace std; int main() {int array[100] {5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6,…

鸿蒙Lottie动画-实现控制动画的播放、暂停、倍速播放、播放顺序

介绍 本示例展示了lottie对动画的操作功能。引入Lottie模块&#xff0c;实现控制动画的播放、暂停、倍速播放、播放顺序、播放到指定帧停止或从指定帧开始播放、侦听事件等功能&#xff0c;动画资源路径必须是json格式。 效果预览 使用说明&#xff1a; 进入页面默认开始201…

Python人工智能应用---中文分词词频统计

目录 1.中文分词 2.循环分别处理列表 &#xff08;1&#xff09;分析 &#xff08;2&#xff09;代码解决 3.词袋模型的构建 &#xff08;1&#xff09;分析需求 &#xff08;2&#xff09;处理分析 1.先实现字符串的连接 2.字符串放到新的列表里面 4.提取高频词语 &…

一些好玩的东西

这里写目录标题 递归1.递归打印数组和链表?代码实现原理讲解二叉树的 前 中 后 序位置 参考文章 递归 1.递归打印数组和链表? 平常我们打印数组和链表都是 迭代 就好了今天学到一个新思路–>不仅可以轻松正着打印数组和链表 , 还能轻松倒着打印(用的是二叉树的前中后序遍…

Linux基础篇:Linux第三方软件仓库——可以让Linux变得有趣的软件仓库

Linux第三方软件仓库——可以让Linux变得有趣的软件仓库 一、epel源介绍 EPEL&#xff08;Extra Packages for Enterprise Linux&#xff09;源是一个由Fedora项目组维护的第三方软件仓库&#xff0c;为企业级Linux发行版&#xff08;如Red Hat Enterprise Linux&#xff08;…

清明作业 c++

1.封装一个类&#xff0c;实现对一个数求累和阶乘质数 #include <iostream>using namespace std; int mproduct(int a){if(a>1){return a*mproduct((a-1));}else{return 1;} } class number{int a; public:number():a(5){};number(int a):a(a){}void set(int a){thi…

ChatGLM-6B实战微调(P-tuning-v2、LORA)

硬件准备 GPU: NVIDIA Tesla A30 24GB python 3.10 pytorch 1.11 transformers 4.27.1 实验环境 Change your pip source pip config set global.extra-index-url https://pypi.tuna.tsinghua.edu.cn/simple # Writing to /opt/conda/pip.conf pip config set global.inde…

浏览器工作原理与实践--DOM树:JavaScript是如何影响DOM树构建的

在上一篇文章中&#xff0c;我们通过开发者工具中的网络面板&#xff0c;介绍了网络请求过程的几种性能指标以及对页面加载的影响。 而在渲染流水线中&#xff0c;后面的步骤都直接或者间接地依赖于DOM结构&#xff0c;所以本文我们就继续沿着网络数据流路径来介绍DOM树是怎么生…

MATLAB实现数值求解高阶常微分方程组

一、高阶常微分方程组 高阶常微分方程是指包含多个高阶常微分方程的系统。这些方程通常涉及多个未知函数及其高阶导数。解决高阶常微分方程组通常比解决单个高阶常微分方程更为复杂&#xff0c;因为需要同时考虑多个方程和多个未知函数之间的关系。 一般来说&#xff0c;解决…

【PyQt5篇】使用QtDesigner添加控件和槽

文章目录 &#x1f354;使用QtDesigner进行设计&#x1f6f8;在代码中添加信号和槽 &#x1f354;使用QtDesigner进行设计 我们首先使用QtDesigner设计界面 得到代码login.ui <?xml version"1.0" encoding"UTF-8"?> <ui version"4.0&q…

金融中的数学模型

平稳时间序列 时间序列的基本统计特性&#xff0c;如均值、方差和自相关等&#xff0c;在时间上不随时间的推移而发生显著的变化。 平稳时间序列通常具有以下特征&#xff1a; 均值不随时间变化&#xff1a;序列的均值在时间上保持恒定。方差不随时间变化&#xff1a;序列的…

CSS属性计算逻辑

CSS 属性计算逻辑 首先&#xff0c;假设在 HTML 中有这么一段代码&#xff0c;在 body 中有一个 h1 标题&#xff1a; <body><h1>这是一个h1标题</h1> </body>目前我们没有设置该 h1 的任何样式&#xff0c;但是却能看到该 h1 有一定的默认样式&…

2024 年最新使用 Python 部署腾讯云服务器搭建企业微信机器人应用详细教程

企业微信机器人是一种可以在企业微信工作群中执行特定任务的自动化工具。它具备丰富的功能&#xff0c;可以帮助企业提高团队协作效率&#xff0c;简化工作流程&#xff0c;并为员工提供更好的工作体验。 获取企业 ID 信息 企业信息页面链接地址&#xff1a;https://work.wei…

Xshell Mobaxterm等终端工具连接不上服务器,显示 SSH服务器拒绝密码。请再试一次。解决办法

问题解决办法&#xff1a; &#xff08;1&#xff09;需要查看配置SSH密钥时&#xff0c;输入的password密码和当前users_name cd /home/: 查看当前系统下的用户名 注意上图中的登录名是服务器端linux下自己设置的user_name用户名&#xff1a; 所以需要将fl改为&#xff1a…

CCIE-10-IPv6-TS

目录 实验条件网络拓朴 环境配置开始Troubleshooting问题1. R25和R22邻居关系没有建立问题2. 去往R25网络的下一跳地址不存在、不可用问题3. 去往目标网络的下一跳地址不存在、不可用 实验条件 网络拓朴 环境配置 在我的资源里可以下载&#xff08;就在这篇文章的开头也可以下…

《Java面试自救指南》(专题三)数据库

文章目录 一条sql语句的查询流程有哪些数据库存储引擎&#xff0c;各自的区别数据库的三大范式事务的四大特性&#xff08;含隔离级别&#xff09;MySQL四种隔离机制的底层实现&#xff08;如何解决幻读 &#xff09;MySQL有哪几种锁&#xff0c;分别怎么实现数据库中有哪些索引…

Kubernetes学习笔记8

Kubernetes集群客户端工具kubectl 我们已经能够部署Kubernetes了&#xff0c;那么我们如何使用Kubernetes集群运行企业的应用程序呢&#xff1f;那么&#xff0c;我们就需要使用命令行工具kubectl。 学习目标&#xff1a; 了解kubectl 命令帮助方法 了解kubectl子命令使用分…

传统海外仓的管理模式有什么缺点?使用位像素海外仓系统的海外仓有什么优势?

传统的海外仓管理模式主要需要大量的人工操作和相对简单的信息化手段进行仓库的日常运营。因此&#xff0c;传统海外仓的运作比较依赖仓库员工的手工记录、核对和处理各种仓储和物流信息。 然而&#xff0c;传统海外仓管理模式通常存在一些缺点&#xff1a; 效率低下 因为需…

算法之美:缓存数据淘汰算法分析及分解实现

在设计一个系统的时候&#xff0c;由于数据库的读取速度远小于内存的读取速度&#xff0c;那么为加快读取速度&#xff0c;需先将一部分数据加入到内存中&#xff08;该动作称为缓存&#xff09;&#xff0c;但是内存容量又是有限的&#xff0c;当缓存的数据大于内存容量时&…