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,一经查实,立即删除!

相关文章

JS第九天

今天是第九天&#xff0c;学习了JS中的设置日期和倒计时&#xff0c;计时器以及验证码倒计时&#xff0c;那么话不多说我们开始今天的学习吧 一、日期设置 1.1日期创建 调用 new Date() 来创建一个新的 Date 对象。在调用时可以带有一些参数&#xff0c;创建一个 Date 对象&…

[ 云计算 | 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…

c#编程基础学习之数组

目录 数组排序数组System.Linq 命名空间其他创建数组的方法 数组 数组用于在单个变量中存储多个值&#xff1b; 声明数组需定义带方括号[]的变量类型&#xff1b; string[] cars {"MARRY", "BOB", "Far", "Aada"}; //字符串数组 in…

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…

【嵌入式学习】FreeRTOS day04.05

练习 1.总结二进制信号量和计数型信号量的区别&#xff0c;以及他们的使用场景。 二进制信号量&#xff1a;信号量的数值只有0和1&#xff08;用于共享资源的访问&#xff09; 计数性信号量&#xff1a;计数型信号量的值一般是大于或者等于2&#xff08;生产者和消费者模型&am…

软件设计原则:开闭原则

定义 开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;是面向对象设计的基本原则之一&#xff0c;由 Bertrand Meyer 提出。它指出软件实体&#xff08;类、模块、函数等&#xff09;应该对扩展开放&#xff0c;对修改封闭。这意味着软件应该设计成在不修改现有代…

如何从vue项目界面上看出来node.js版本

一&#xff1a; 要从 Vue 项目界面上看出当前使用的 Node.js 版本&#xff0c;你可以通过以下几种方式进行查看&#xff1a; 开发者工具&#xff1a;在浏览器中打开 Vue 项目&#xff0c;并打开开发者工具&#xff08;一般是按下 F12 键&#xff09;。在开发者工具的控制台选项…

【复读EffectiveC++16】条款16:成对使用new和delete时要采取相同形式

条款16&#xff1a;成对使用new和delete时要采取相同形式 此条款&#xff0c;依然是针对对象管理资源的补充&#xff0c;内容分为三个部分&#xff1a; 错误案例为什么要采取相同形式需要注意什么 一、错误案例 取原书的例子&#xff1a; std::string* stringArray new s…

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

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

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

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

SpringBoot面试题积累

面试题15道&#xff1a;腾讯二面&#xff1a;SpringBoot 有几种读取配置文件的方式&#xff1f;你详细说一下每种方式是怎么使用的 &#xff1f;_哔哩哔哩_bilibili 1. SpringBoot 有几种读取配置文件的方式&#xff1f;你详细说一下每种方式是怎么使用的 &#xff1f; 2. 为…

【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 有一定的默认样式&…