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.hqwc.cn/news/597140.html

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

相关文章

信息论基础:串联信道

串联信道 大学时候看过一期湖南卫视《快乐大本营》&#xff0c;那时候的主持人是何炅和李湘。节目的一个环节是邀请五名观众上台做猜谜游戏。五人带上耳机&#xff0c;坐在一排椅子上&#xff0c;两两中间隔着挡板&#xff0c;好像并排在一起上厕所。李湘把一部电影的名字写在…

C语言和C++的 assert 和 static_assert

assert和static_assert用法 assert assert 是一个宏定义&#xff0c;用于在运行时进行断言&#xff08;assertion&#xff09;。它在 <assert.h>&#xff08;C语言&#xff09;或 &#xff08;C语言&#xff09;头文件中定义。 assert 宏接受一个表达式作为参数。…

先锋阀门带您领略2024第13届生物发酵装备展

参展企业介绍 温州先锋阀门有限公司坐落于【中国阀门城】---温州市龙湾&#xff0c;是一家集研发、设计、制造、销售和服务为一体的科技创新型企业。拥有10多项国家专利&#xff0c;三个产品荣获中国通用机械工业协会颁发的(中国国际阀门博览会)银奖称号&#xff0c;部分产品还…

第十四届蓝桥杯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,…

C语言解决汉诺塔问题

背景 首先带大家了解一下汉诺塔问题 汉诺塔是一个典型的函数递归问题&#xff0c;汉诺塔描述了这样的场景&#xff0c;有三个柱子&#xff0c;A,B,C&#xff0c;A柱为起始柱&#xff0c;在A柱上面有若干大小不同的盘子&#xff0c;最下面的最大&#xff0c;最上面的最小&#x…

状态机与重传机制

CP协议比较复杂&#xff0c;接下来分两篇文章浅要介绍TCP中的一些要点。 本文介绍TCP的状态机与重传机制&#xff0c;下文讲解流量控制与拥塞控制。 前置知识 一些网络基础 TCP在网络OSI的七层模型中的第四层——Transport层&#xff0c;IP在第三层——Network层&#xff0c…

高个子男生穿什么裤子好看?高个子男装店铺推荐

大家每隔一段时间都需要购置新的衣服&#xff0c;但是由于目前市面上的服装品牌非常多&#xff0c;而且质量也参差不齐&#xff0c;不少新闻都经常报道许多衣服材质面料不合格&#xff0c;出去闷热不透气、不耐洗等问题。 所以今天就从面料、风格等方面进行测评&#xff0c;并…

FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习

FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习 临界段代码保护 所谓临界段代码保护就是指必须完成运行&#xff0c;不能被打断的代码段。比如需要严格按照时序除初始化的外设&#xff1a;IIC、SPI&#xff0c;再或者因为系统自身需求和用户需求。 FreeRTOS 在进入临界…

SMW200A罗德与施瓦茨SMW200A信号发生器

181/2461/8938产品概述&#xff1a; SMW200A是开发新型宽带通信系统&#xff0c;验证3G和4G基站&#xff0c;以及需数字调制信号的理想信号发生器。 SMW200A 矢量信号发生器 具有内部基带、高达2 GHz的I/Q调制带宽可以满足第4代和第5代标准(例如&#xff0c;5G、LTE-Advanced…

比例多路阀控制器US-DAT2-A

液压比例阀放大器是一种用于精确控制液压系统的技术&#xff0c;它通过电信号实现对液压阀的连续量控制。接收来自控制器的低功率电信号&#xff0c;然后将其放大并转换为高功率信号&#xff0c;这个高功率信号足以驱动比例阀的开启和关闭。这种技术允许进行非常精细的调节&…

想要实现自动化批量抓取淘宝商品数据店铺数据订单数据的看过来(淘宝开放平台API调用实例)

item_get 获得淘宝商品详情 获取API测试keyitem_get_pro 获得淘宝商品详情高级版item_review 获得淘宝商品评论item_fee 获得淘宝商品快递费用item_password 获得淘口令真实urlitem_list_updown 批量获得淘宝商品上下架时间seller_info 获得淘宝店铺详情item_search 按关键字…

过亿级别的用户数据如何检查用户名是否存在?

目录 引言用户名存在性检查的挑战用户规模庞大带来的性能挑战数据一致性与并发性问题防止恶意行为的挑战 常见的解决方案基于数据库的方案基于缓存的方案基于分布式系统的方案基于搜索引擎的方案 案例分析与实践经验分享社交媒体平台的用户名检查方案 引言 随着互联网的普及和数…