C++设计模式之原型模式(Prototype)

news2025/2/25 15:33:52

[C++]22种设计模式的C++实现大纲

文章目录

    • 定义
    • 别名
    • 前言
      • 1. 问题
      • 2. 解决方案
    • 结构
      • 1. 基本实现
      • 2. 原型注册表实现
    • 适用场景
    • 实现方式
    • 优点
    • 缺点
    • 与其他模式的关系
    • 实例

定义

原型是一种创建型设计模式,使你能够复制已有对象,而又无需使代码依赖它们所属的类。

别名

克隆(Clone)。

前言

1. 问题

如果你有一个对象,并希望生成与其完全相同的一个复制品,你该如何实现呢?首先,你必须新建一个属于相同类的对象。然后,你必须遍历原始对象的所有成员变量,并将成员变量值复制到新对象中。

不错!但有个小问题。并非所有对象都能通过这种方式进行复制,因为有些对象可能拥有私有成员变量,它们在对象本身以外是不可见的。

直接复制还有另外一个问题。因为你必须知道对象所属的类才能创建复制品,所以代码必须依赖该类。即使你可以接受额外的依赖性,那还有另外一个问题:有时你只知道对象所实现的接口,而不知道其所属的具体类,比如可向方法的某个参数传入实现了某个接口的任何对象。

2. 解决方案

原型模式将克隆过程委派给被克隆的实际对象。模式为所有支持克隆的对象声明了一个通用接口,该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。通常情况下,这样的接口中仅包含一个“克隆”方法。

所有的类对“克隆”方法的实现都非常相似。该方法会创建一个当前类的对象,然后将原始对象所有的成员变量值复制到新建的类中。你甚至可以复制私有成员变量,因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。

支持克隆的对象即为原型。当你的对象有几十个成员变量和几百种类型时,对其进行克隆甚至可以代替子类的构造。

在这里插入图片描述

其运作方式如下:创建一系列不同类型的对象并不同的方式对其进行配置。如果所需对象与预先配置的对象相同,那么你只需克隆原型即可,无需新建一个对象。

结构

1. 基本实现

在这里插入图片描述

  1. 原型(Prototype)接口将对克隆方法进行声明。在绝大多数情况下,其中只会有一个名为clone 克隆的方法。
  2. 具体原型(Concrete Prototype)类将实现克隆方法。除了将原始对象的数据复制到克隆体中之外,该方法有时还需处理克隆过程中的极端情况,例如克隆关联对象和梳理递归依赖等等。
  3. 客户端(Client)可以复制实现了原型接口的任何对象。

2. 原型注册表实现

在这里插入图片描述

原型注册表(Prototype Registry)提供了一种访问常用原型的简单方法,其中存储了一系列可供随时复制的预生成对象。最简单的注册表原型是一个「名称 → 原型」的哈希表。但如果需要使用名称以外的条件进行搜索,你可以创建更加完善的注册表版本。

适用场景

● 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。

这一点考量通常出现在代码需要处理第三方代码通过接口传递过来的对象时。即使不考虑代码耦合的情况,你的代码也不能依赖这些对象所属的具体类,因为你不知道它们的具体信息。原型模式为客户端代码提供一个通用接口,客户端代码可通过这一接口与所有实现了克隆的对象进行交互,它也使得客户端代码与其所克隆的对象具体类独立开来。

● 如果子类的区别仅在于其对象的初始化方式,那么你可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象。

在原型模式中,你可以使用一系列预生成的、各种类型的对象作为原型。客户端不必根据需求对子类进行实例化,只需找到合适的原型并对其进行克隆即可。

实现方式

  1. 创建原型接口,并在其中声明“克隆”方法。如果你已有类层次结构,则只需在其所有类中添加该方法即可。
  2. 原型类必须另行定义一个以该类对象为参数的构造函数。构造函数必须复制参数对象中的所有成员变量值到新建实体中。如果你需要修改子类,则必须调用父类构造函数,让父类复制其私有成员变量值。如果编程语言不支持方法重载,那么你可能需要定义一个特殊方法来复制对象数据。在构造函数中进行此类处理比较方便,因为它在调用new 运算符后会马上返回结果对象。
  3. 克隆方法通常只有一行代码: 使用new 运算符调用原型版本的构造函数。注意,每个类都必须显式重写克隆方法并使用自身类名调用new 运算符。否则, 克隆方法可能会生成父类的对象。
  4. 你还可以创建一个中心化原型注册表,用于存储常用原型。你可以新建一个工厂类来实现注册表,或者在原型基类中添加一个获取原型的静态方法。该方法必须能够根据客户端代码设定的条件进行搜索。搜索条件可以是简单的字符串,或者是一组复杂的搜索参数。找到合适的原型后,注册表应对原型进行克隆,并将复制生成的对象返回给客户端。最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。

优点

● 你可以克隆对象,而无需与它们所属的具体类相耦合。
● 你可以克隆预生成原型,避免反复运行初始化代码。
● 你可以更方便地生成复杂对象。
● 你可以用继承以外的方式来处理复杂对象的不同配置。

缺点

克隆包含循环引用的复杂对象可能会非常麻烦。

与其他模式的关系

● 在许多设计工作的初期都会使用工厂方法(较为简单,而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂、原型或生成器(更灵活但更加复杂)。
● 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
● 原型可用于保存命令的历史记录。
● 大量使用组合和装饰的设计通常可从对于原型的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造。
● 原型并不基于继承,因此没有继承的缺点。另一方面,原型需要对被复制对象进行复杂的初始化。工厂方法基于继承,但是它不需要初始化步骤。
● 有时候原型可以作为备忘录的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便地重建。
● 抽象工厂、生成器和原型都可以用单例来实现。

实例

Prototype.h:

#ifndef PROTOTYPE_H_
#define PROTOTYPE_H_

// 抽象原型类
class Object {
 public:
    virtual Object* clone() = 0;
};

#endif  // PROTOTYPE_H_

ConcretePrototype.h:

#ifndef CONCRETE_PROTOTYPE_H_
#define CONCRETE_PROTOTYPE_H_

#include <iostream>
#include <string>
#include "Prototype.h"


// 邮件的附件
class Attachment {
 public:
    void set_content(std::string content) {
        content_ = content;
    }
    std::string get_content() {
        return content_;
    }

 private:
    std::string content_;
};

// 具体原型: 邮件类
class Email : public Object {
 public:
    Email() {}
    Email(std::string text, std::string attachment_content) : text_(text), attachment_(new Attachment()) {
        attachment_->set_content(attachment_content);
    }
    ~Email() {
        if (attachment_ != nullptr) {
            delete attachment_;
            attachment_ = nullptr;
        }
    }

    void display() {
        std::cout << "------------查看邮件------------" << std::endl;
        std::cout << "正文: " << text_ << std::endl;
        std::cout << "邮件: " << attachment_->get_content() << std::endl;
        std::cout << "------------查看完毕------------" << std::endl;
    }

    // 深拷贝
    Email* clone() override {
        return new Email(this->text_, this->attachment_->get_content());
    }

    void changeText(std::string new_text) {
        text_ = new_text;
    }

    void changeAttachment(std::string content) {
        attachment_->set_content(content);
    }

 private:
    std::string text_;
    Attachment *attachment_ = nullptr;
};

#endif  // CONCRETE_PROTOTYPE_H_

main.cpp:

#include "ConcretePrototype.h"

#include <cstdio>

int main() {
    Email* email = new Email("最初的文案", "最初的附件");
    Email* copy_email = email->clone();
    copy_email->changeText("新文案");
    copy_email->changeAttachment("新附件");
    std::cout << "original email:" << std::endl;
    email->display();
    std::cout << "copy email:" << std::endl;
    copy_email->display();

    delete email;
    delete copy_email;
}

编译运行:

$g++ -g main.cpp -o prototype -std=c++11
$./prototype 
original email:
------------查看邮件------------
正文: 最初的文案
邮件: 最初的附件
------------查看完毕------------
copy email:
------------查看邮件------------
正文: 新文案
邮件: 新附件
------------查看完毕------------

[C++]22种设计模式的C++实现大纲跳转

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

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

相关文章

利用fabric绘画矩形和多边形

需求在一张图片上标注矩形和多边形&#xff0c;支持回显&#xff1b; fabric版本&#xff1a;4.6.0&#xff1b; Fabric.js 是一个功能强大且操作简单的 Javascript HTML5 canvas 工具库。 官方文档 参考链接 组件代码drawer.vue createUuid 是为了让每一个图形有自己的id&…

【 Python 全栈开发 - 语法基础篇 - 20 】数据可视化

文章目录 一、数据可视化二、pandas1. 折线图2. 散点图3. 柱状图4. 饼图 三、matplotlib1. 折线图2. 散点图3. 柱状图4. 饼图 四、seaborn1. 安装和导入Seaborn2. 加载数据集3. 绘制散点图4. 绘制直方图5. 绘制核密度图6. 绘制条形图7. 绘制热力图 五、plotly安装plotly创建图表…

chatgpt赋能python:如何重新运行Python程序:完整指南

如何重新运行Python程序&#xff1a;完整指南 Python是最受欢迎的编程语言之一&#xff0c;因为其语法简单易懂&#xff0c;使得编写高效可读性代码更加轻松。但在编程过程中经常会出现需要重新运行程序的情况&#xff0c;本文将为您介绍如何重新运行Python程序。 重新运行Py…

chatgpt赋能python:Python如何降低memory的方法

Python如何降低memory的方法 Python已经成为了世界上最流行的编程语言之一&#xff0c;它在开发web应用、机器学习、数据分析等领域中拥有广泛的应用。然而&#xff0c;由于Python的内存管理机制&#xff0c;可能会导致程序的内存占用过高&#xff0c;影响系统的性能。在本文中…

excel文档翻译软件怎么使用?告诉你怎么翻译整个excel文档

excel是一款电子表格软件&#xff0c;广泛应用于数据分析、统计和管理等领域。然而&#xff0c;当我们需要处理包含其他语言的excel文档时&#xff0c;可能会遇到语言障碍。不用担心&#xff0c;现在有一些方便的软件可以帮助我们轻松翻译excel文档。今天我们就一起来看看excel…

UniApp个人总结:新建页面大汇总

文章目录 往期回顾正文本篇目标环境安装如何新建模板页面页面布局推荐新建模板文件 总结 往期回顾 uniapp 踩坑记录 uni.$on为什么不能修改data里面的数据 uniApp页面通讯大汇总&#xff0c;如何页面之间传值 uniApp 页面通讯统一解决方案 uniapp sqlite 数据库操作封装 un…

一致性模型

首先明确一下分布式的组成定义&#xff0c;为下面打好铺垫 都做到了让系统“表现得像只有一个副本”。它们的不同在于&#xff0c;前一种排序遵循了不同用户的操作的时间先后顺序&#xff0c;而后一种排序没有。实际上&#xff0c;如果我们要求系统满足线性一致性&#xff0c;就…

什么是创新,为何如此难,又能解决啥-非AI撰写

什么项目创新&#xff1f;机器人技术创新&#xff1f;能解决下文中的问题呢&#xff1f; 这是近两个月以来&#xff0c;也许唯一一篇&#xff0c;我自己码字写的博客。 有感于一些课程&#xff0c;比如&#xff1a; 这一类课程最为典型的特点就是课程名称上有“创新”这两个字…

Matlab基础入门

Matlab简介 矩阵实验室&#xff08;matrix&laboratory&#xff09; R2022a&#xff1a;2022上半年的版本 R2022b&#xff1a;2022下半年的版本 Matlab界面 命令行窗口 与使用者直接进行交互&#xff0c;相当于一个计算器 >> 11ans 2 >> 5-2ans 3 >&…

再谈如何在python3.10等环境中搞崩wordcloud

如果你能搞崩python中的wordcloud&#xff0c;让它无法运行&#xff0c;基本上python的第三方依赖库的问题就好简单了。我们以搞崩python3.8 为例。 让python3.8运行词语图程序的时候出现truetype fonts 错误&#xff01;&#xff01;&#xff01; 方法如下&#xff1a; pyth…

chatgpt赋能python:Python如何运行两次

Python如何运行两次 Python是一种高级编程语言&#xff0c;非常受欢迎&#xff0c;因为它易于学习&#xff0c;简单易用&#xff0c;开放性强&#xff0c;而且功能强大。Python常用于处理各种任务&#xff0c;包括数据分析、Web应用开发、人工智能、自动化测试等。此外&#x…

噪声参数估计相关

文章目录 噪声参数估计相关1. Estimation Of Signal Dependent Noise Parameters From A Single Image2. Practical Poissonian-Gaussian noise modeling and fitting for single-image raw-data3. Simplified noise model parameter estimation for signal-dependent noise4. …

ipad手写笔哪款好?性价比高的触控笔

在现代人的生活中&#xff0c;电容笔的身影随处可见&#xff0c;随着电容笔的广泛&#xff0c;其品牌和种类也越来越多&#xff0c;更多的人群追求性价比&#xff0c;苹果产品深受大家欢迎&#xff0c;但是大多数人都是被价格劝退&#xff0c;下面整理几款适合iPad用的平替电容…

Flask开发简易网站疑难点梳理

文章目录 整体总结创建项目独立的python环境windows下python独立环境目录结构linux下python独立环境目录结构 大概需要安装的第三方库使用websockt实现python代码与html界面的通讯界面F12中看到提示连接成功后立马连接关闭。 linux下数据库查询异常初次登录web的时候背景图片和…

ERP的需求分析(下)

目录 1、采购管理 2、生产订单模块 3、仓库管理 4、查询与分析

Golang处理内存溢出

背景&#xff1a; 最近系统在压测过程中发现主程序在并发增大后会出现主程序闪退现象&#xff0c;几经波折&#xff0c;认为有可能是内存溢出引起的 正好对 Golang 里分析 dump 这块还没怎么涉及&#xff0c;借此契机研究一下。 前言&#xff1a; 查看社区后&#xff0c;发现…

微信小程序原生开发功能合集十七:echarts使用及分享功能介绍

本章实现echarts的引入及使用,演示使用echarts创建界面图表。实现界面分享功能介绍,包括好友分享及朋友圈分享等。 echarts官网: https://echarts.apache.org/zh/index.html echarts-小程序: https://github.com/ecomfe/echarts-for-weixin   另外还提供小程序开发基础知…

APP外包开发的第三方代码库

在APP的开发过程中有很多好用的第三方库&#xff0c;这些第三方库代码质量高&#xff0c;已经在很多的项目实际使用过&#xff0c;因此在开发APP时是非常好的选择。第三方库可以减轻开发人员工作量&#xff0c;也是开发人员必须要关注的辅助代码。今天和大家分享一些常用的第三…

MATLAB 之 数据插值、曲线拟合和数值微分

这里写目录标题 一、数据插值1. 一维数据插值2. 二维数据插值 二、曲线拟合1. 曲线拟合原理2. 曲线拟合的实现 三、数值微分1. 数值差分与差商2. 数值微分的实现 一、数据插值 在工程测量和科学实验中&#xff0c;所得到的数据通常都是离散的。如果要得到这些离散点以外的其他…

python语言GDAL读取所在点的像素值

由于项目需求&#xff0c;需要用到开源的软件开发&#xff0c;就开始研究GDAL&#xff0c;这个开源库&#xff0c;目前来说&#xff0c;调用GDAL库&#xff0c;最方便的语言还是python &#xff0c;简单记录下&#xff0c;用python语言做GDAL开发的一些东西吧。一个在开发中经常…