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

通俗易懂地解读原型模式(Prototype Pattern)

什么是原型模式?

原型模式是一种创建型设计模式,它让我们可以通过复制(克隆)一个现有的对象,快速生成一个新的对象,而不需要再次通过类的实例化来创建。

用大白话来说:

如果你已经有一个模子(原型),你就可以用这个模子快速复制出新对象,就像用模型复制黏土雕塑一样。这样,你不需要每次都从零开始重新雕一个。


为什么需要原型模式?
  1. 节省时间和资源
    • 如果对象的创建很复杂(比如需要加载文件、调用外部服务等),直接复制一个现成的对象会快很多。
  2. 动态创建对象
    • 有时候,我们在运行时并不知道需要创建什么类型的对象,使用原型模式可以很灵活地克隆已有的对象。
  3. 避免类的依赖
    • 不需要在代码中直接实例化某个类,只需复制一个已有的实例即可。

举个例子

假如你在一个图形设计软件中设计了一些图形(比如圆形和矩形)。这些图形可能需要很多属性:大小、颜色、边框等。现在你需要快速生成这些图形的副本,而不是每次都手动创建。


原型模式的实现

下面我们通过一个简单的例子,演示如何用 C++ 实现原型模式:
模拟一个图形系统,通过原型模式实现图形(圆形、矩形)的快速复制。


完整代码
#include <iostream>
#include <string>
#include <memory> // 智能指针管理

// 抽象原型类
class Shape {
public:
    virtual ~Shape() = default;

    // 克隆方法,返回一个复制的对象
    virtual std::shared_ptr<Shape> clone() const = 0;

    // 显示图形信息
    virtual void draw() const = 0;
};

// 圆形类
class Circle : public Shape {
public:
    Circle(double radius) : radius_(radius) {}

    // 实现克隆方法
    std::shared_ptr<Shape> clone() const override {
        return std::make_shared<Circle>(*this); // 使用复制构造函数
    }

    // 显示圆形信息
    void draw() const override {
        std::cout << "绘制一个圆,半径为:" << radius_ << "\n";
    }

private:
    double radius_; // 半径
};

// 矩形类
class Rectangle : public Shape {
public:
    Rectangle(double width, double height) : width_(width), height_(height) {}

    // 实现克隆方法
    std::shared_ptr<Shape> clone() const override {
        return std::make_shared<Rectangle>(*this); // 使用复制构造函数
    }

    // 显示矩形信息
    void draw() const override {
        std::cout << "绘制一个矩形,宽为:" << width_ << ",高为:" << height_ << "\n";
    }

private:
    double width_;  // 矩形的宽
    double height_; // 矩形的高
};

// 客户端代码
int main() {
    // 创建一个圆形原型
    std::shared_ptr<Shape> circlePrototype = std::make_shared<Circle>(10.0);

    // 创建一个矩形原型
    std::shared_ptr<Shape> rectanglePrototype = std::make_shared<Rectangle>(20.0, 15.0);

    // 克隆圆形
    std::shared_ptr<Shape> clonedCircle = circlePrototype->clone();

    // 克隆矩形
    std::shared_ptr<Shape> clonedRectangle = rectanglePrototype->clone();

    // 显示克隆的图形
    clonedCircle->draw();    // 输出:绘制一个圆,半径为:10
    clonedRectangle->draw(); // 输出:绘制一个矩形,宽为:20,高为:15

    return 0;
}

运行结果

运行以上代码后,将输出以下结果:

绘制一个圆,半径为:10
绘制一个矩形,宽为:20,高为:15

代码逐步解析
  1. Shape 类:

    • 它是一个抽象原型类,定义了一个clone()方法,让所有子类实现复制的逻辑。
    • 同时,draw()方法用来显示图形信息。
  2. CircleRectangle 类:

    • 它们是具体的原型类,继承自Shape
    • 通过实现clone()方法,使用复制构造函数完成克隆操作。
    • draw()方法显示图形的具体属性。
  3. main 函数(客户端):

    • 客户端创建了一些原型对象(圆形和矩形)。
    • 使用这些原型的clone()方法,快速生成新的对象,并通过draw()方法显示它们。

通俗解释代码

  • clone() 就像一个“复印机”,你放进去一个原型对象(比如一个圆形),它会帮你复印出一个一模一样的新对象。
  • 不同的图形类(如圆形、矩形)都有自己的复制逻辑,但客户端只需要调用clone()就可以完成复制,而不需要关心复制细节。
  • 最终,你可以轻松地复制出大量类似的对象,而无需每次手动创建。

原型模式的优缺点
优点
  1. 提高效率:
    • 复制一个现成的对象比重新初始化一个对象快得多,特别是对象初始化代价很高时(比如加载资源或复杂计算)。
  2. 运行时动态创建:
    • 不需要在编译时确定对象类型,运行时可以基于原型创建实例,灵活性更高。
  3. 减少耦合:
    • 客户端只需要调用clone()方法,而无需关心具体对象的创建细节。
缺点
  1. 复杂的深拷贝:
    • 如果对象有嵌套结构(比如对象内部还有对象),需要实现复杂的深拷贝逻辑。
  2. 内存管理复杂:
    • 如果对象包含动态分配的资源,克隆时需要特别注意释放这些资源,否则可能导致内存泄漏。

适用场景
  1. 对象创建成本高:
    • 比如包含大量数据或需要复杂初始化的对象,可以通过克隆快速复制。
  2. 需要创建相似对象:
    • 比如游戏中的各种怪物、道具等,它们的基础属性相同,但某些细节可以在克隆后修改。
  3. 动态决定对象类型:
    • 如果程序在运行时需要根据情况创建对象,可以通过原型模式更灵活地实现。

总结

原型模式是一种非常实用的设计模式,特别是在需要创建大量相似对象或对象创建成本较高的情况下。通过clone()方法,我们可以轻松地复制一个对象,避免了重新初始化的麻烦。在实际开发中,原型模式广泛用于游戏开发、图形处理、文档复制等场景。希望通过这个简单的图形示例,你已经能够理解并掌握这一设计模式!

完整代码块

#include <iostream>
#include <string>
#include <memory> // 用于智能指针管理

// 抽象原型类
class Shape {
public:
    virtual ~Shape() = default;

    // 克隆方法,返回一个复制的对象
    virtual std::shared_ptr<Shape> clone() const = 0;

    // 显示图形信息
    virtual void draw() const = 0;

    // 设置名称,用于标识不同对象
    void setName(const std::string& name) {
        name_ = name;
    }

    // 获取对象名称
    std::string getName() const {
        return name_;
    }

private:
    std::string name_;
};

// 圆形类
class Circle : public Shape {
public:
    Circle(double radius) : radius_(radius) {}

    // 实现克隆方法
    std::shared_ptr<Shape> clone() const override {
        return std::make_shared<Circle>(*this); // 使用复制构造函数
    }

    // 显示圆形信息
    void draw() const override {
        std::cout << "圆形对象 [" << getName() << "],半径为:" << radius_ << "\n";
    }

private:
    double radius_; // 半径
};

// 矩形类
class Rectangle : public Shape {
public:
    Rectangle(double width, double height) : width_(width), height_(height) {}

    // 实现克隆方法
    std::shared_ptr<Shape> clone() const override {
        return std::make_shared<Rectangle>(*this); // 使用复制构造函数
    }

    // 显示矩形信息
    void draw() const override {
        std::cout << "矩形对象 [" << getName() << "],宽为:" << width_ << ",高为:" << height_ << "\n";
    }

private:
    double width_;  // 矩形的宽
    double height_; // 矩形的高
};

// 客户端代码
int main() {
    // 创建原始圆形对象
    std::shared_ptr<Shape> circlePrototype = std::make_shared<Circle>(10.0);
    circlePrototype->setName("原始圆形");

    // 创建原始矩形对象
    std::shared_ptr<Shape> rectanglePrototype = std::make_shared<Rectangle>(20.0, 15.0);
    rectanglePrototype->setName("原始矩形");

    // 克隆圆形
    std::shared_ptr<Shape> clonedCircle = circlePrototype->clone();
    clonedCircle->setName("拷贝圆形");

    // 克隆矩形
    std::shared_ptr<Shape> clonedRectangle = rectanglePrototype->clone();
    clonedRectangle->setName("拷贝矩形");

    // 显示原始和克隆的图形
    std::cout << "=== 原始对象 ===\n";
    circlePrototype->draw();
    rectanglePrototype->draw();

    std::cout << "\n=== 拷贝对象 ===\n";
    clonedCircle->draw();
    clonedRectangle->draw();

    return 0;
}

运行结果

运行以上代码后,输出如下:

=== 原始对象 ===
圆形对象 [原始圆形],半径为:10
矩形对象 [原始矩形],宽为:20,高为:15

=== 拷贝对象 ===
圆形对象 [拷贝圆形],半径为:10
矩形对象 [拷贝矩形],宽为:20,高为:15

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

跨平台WPF框架Avalonia教程 十三

AutoCompleteBox 自动补全输入框 自动补全输入框提供了一个供用户输入的文本框和一个包含可能匹配项的下拉列表。下拉列表会在用户开始输入时显示&#xff0c;并且每输入一个字符&#xff0c;匹配项都会更新。用户可以从下拉列表中选择匹配项。 文本与可能项匹配的方式是可配…

MATLAB实现GARCH(广义自回归条件异方差)模型计算VaR(Value at Risk)

MATLAB实现GARCH(广义自回归条件异方差)模型计算VaR(Value at Risk) 1.计算模型介绍 使用GARCH&#xff08;广义自回归条件异方差&#xff09;模型计算VaR&#xff08;风险价值&#xff09;时&#xff0c;方差法是一个常用的方法。GARCH模型能够捕捉到金融时间序列数据中的波…

力扣 LeetCode 513. 找树左下角的值(Day8:二叉树)

解题思路&#xff1a; 方法一&#xff1a;递归法&#xff08;方法二更好理解&#xff0c;个人更习惯方法二&#xff09; 前中后序均可&#xff0c;实际上没有中的处理 中左右&#xff0c;左中右&#xff0c;左右中&#xff0c;实际上都是左在前&#xff0c;所以遇到的第一个…

Nuget For Unity插件介绍

NuGet for Unity&#xff1a;提升 Unity 开发效率的利器 NuGet 是 .NET 开发生态中不可或缺的包管理工具,你可以将其理解为Unity的Assets Store或者UPM,里面有很多库可以帮助我们提高开发效率。当你想使用一个库,恰好这个库没什么依赖(比如newtonjson),那么下载包并找到Dll直接…

“乐鑫组件注册表”简介

当启动一个新的开发项目时&#xff0c;开发者们通常会利用库和驱动程序等现有的代码资源。这种做法不仅节省时间&#xff0c;还简化了项目的维护工作。本文将深入探讨乐鑫组件注册表的概念及其核心理念&#xff0c;旨在指导您高效地使用和贡献组件。 概念解析 ESP-IDF 的架构…

药房革新:Spring Boot中药实验管理系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

嵌入式 UI 开发的开源项目推荐

嵌入式开发 UI 难吗&#xff1f;你的痛点我懂&#xff01;作为嵌入式开发者&#xff0c;你是否也有以下困扰&#xff1f;设备资源太少&#xff0c;功能和美观只能二选一&#xff1f;调试效率低&#xff0c;每次调整都要反复烧录和测试&#xff1f;开发周期太长&#xff0c;让你…

CTF--php伪协议结合Base64绕过

Base64绕过 在ctf中&#xff0c;base64是比较常见的编码方式&#xff0c;在做题的时候发现自己对于base64的编码和解码规则不是很了解&#xff0c;并且恰好碰到了类似的题目&#xff0c;在翻阅了大佬的文章后记录一下&#xff0c;对于base64编码的学习和一个工具 base64编码是…

基于Java Springboot电影播放平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

国标GB28181摄像机接入EasyGBS国标GB28181设备管理软件:GB28181-2022媒体传输协议解析

随着信息技术的飞速发展&#xff0c;视频监控领域正经历从传统安防向智能化、网络化安防的深刻转变。在这一转变过程中&#xff0c;国标GB28181设备管理软件EasyGBS成为了这场技术变革的重要一环。 GB28181-2022媒体传输协议 媒体传输命令包括实时视音频点播、历史视音频回放/…

Redis-monitor安装与配置

0、前言 压测环境因为隔离原因没法直接查看redis日志跟性能指数&#xff0c;只能通过监控工具查看&#xff0c;使用开源redis-montor监控查看 开源地址&#xff1a; GitCode - 全球开发者的开源社区,开源代码托管平台 1、python环境准备&#xff08;python -v有的忽略&#xff…

windows basic语言学习笔记,批处理命令的简单使用

BAT学习笔记 前言 Windows 命令行中对参数的大小写不敏感&#xff0c;因此 /D 和 /d 的效果完全一致。 1. 代码1&#xff1a;创建目录并复制文件 源代码&#xff1a; echo off REM 创建目标目录&#xff0c;如果不存在 if not exist "C:\h2" (mkdir "C:\h2&q…

5-对象的访问权限

对象的访问权限知识点 对象的分类 在数据库中&#xff0c;数据库的表、索引、视图、缺省值、规则、触发器等等、都可以被称为数据库对象&#xff0c;其中对象主要分为两类 1、模式(schema)对象&#xff1a;模式对象可以理解为一个存储目录、包含视图、索引、数据类型、函数和…

Java Database Connectivity (JDBC + Servlet)

Java Database Connectivity (JDBC)是一个Java API&#xff0c;用于与数据库进行连接和操作。通过JDBC&#xff0c;Java程序可以与各种关系型数据库进行通信&#xff0c;执行SQL查询、更新数据等操作。 一、Java连接数据库两种方式 ​​​​​ ​​ 二、Java中…

[Realtek sdk-3.4.14b] RTL8197FH-VG新增jffs2分区操作说明

sdk说明 ** Gateway/AP firmware v3.4.14b – Aug 26, 2019**  Wireless LAN driver changes as:  Refine WiFi Stability and Performance  Add 8812F MU-MIMO  Add 97G/8812F multiple mac-clone  Add 97G 2T3R antenna diversity  Fix 97G/8812F/8814B MP issu…

鸿蒙多线程开发——线程间数据通信对象01

1、线程间通信 线程间通信指的是并发多线程间存在的数据交换行为。由于ArkTS语言兼容TS/JS&#xff0c;其运行时的实现与其它所有的JS引擎一样&#xff0c;都是基于Actor内存隔离的并发模型提供并发能力。 对于不同的数据对象&#xff0c;在ArkTS线程间通信的行为是有差异的&…

基于单片机的多功能跑步机控制系统

本设计基于单片机的一种多功能跑步机控制系统。该系统以STM32单片机为主控制器&#xff0c;由七个电路模块组成&#xff0c;分别是&#xff1a;单片机模块、电机控制模块、心率检测模块、音乐播放模块、液晶显示模块、语音控制模块、电源模块。其中&#xff0c;单片机模块是整个…

测试工程师如何在面试中脱颖而出

目录 1.平时工作中是怎么去测的&#xff1f; 2.B/S架构和C/S架构区别 3.B/S架构的系统从哪些点去测&#xff1f; 4.你为什么能够做测试这一行&#xff1f;&#xff08;根据个人情况分析理解&#xff09; 5.你认为测试的目的是什么&#xff1f; 6.软件测试的流程&#xff…

PHM技术:基于支持向量机的智能故障诊断 | 行星齿轮箱智能故障诊断

目录 1.数据获取 2.特征提取与选择 3.健康状态识别 1.数据获取 用的行星齿轮箱数据采集自图1中的多级齿轮传动系统实验台中&#xff0c;在实验过程中&#xff0c;分别模拟了8种行星齿轮箱的健康状态&#xff0c;包括正常、第一级太阳轮点蚀、第一级太阳轮齿根裂纹、第一级…

【划分型 DP-约束划分个数】【hard】【阿里笔试】力扣1278. 分割回文串 III

给你一个由小写字母组成的字符串 s&#xff0c;和一个整数 k。 请你按下面的要求分割字符串&#xff1a; 首先&#xff0c;你可以将 s 中的部分字符修改为其他的小写英文字母。 接着&#xff0c;你需要把 s 分割成 k 个非空且不相交的子串&#xff0c;并且每个子串都是回文串…