AI Chat 设计模式:3. 原型模式

news2024/12/26 23:16:52

本文是该系列的第三篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的旁白。

问题列表

  • Q.1 今天我们聊一下原型模式吧
  • A.1
  • Q.2 那写一个实现了深拷贝的例子
  • A.2
  • Q.3 你这里为什么要对构造函数进行深拷贝呢,是否有必要?
  • A.3
  • Q.4 我是说,你的代码中,构造函数存在深拷贝,这是为什么,是否有必要?
  • A.4
  • Q.5 你说,这个例子中的构造函数并没有实现深拷贝。到底有没有?你说话前后矛盾
  • A.5
  • Q.6 比较一下原型模式和工厂设计模式吧
  • A.6
  • Q.7 画一下原型模式的类图
  • A.7

Q.1 今天我们聊一下原型模式吧

A.1

好的,原型模式是一种创建型设计模式,它通过克隆现有的对象来创建新对象。这种模式的关键在于提供一个原型对象,客户端通过克隆原型对象创建出新的对象。这样可以避免手工创建对象的复杂性,并减少了重复的创建过程,提高了系统的效率。

在原型模式中,原型对象是通过 clone 方法进行复制的,因此要实现原型模式,需要满足两个条件:

  1. 实现 Cloneable 接口,该接口中没有任何方法,仅用于指示该对象可以被克隆。
  2. 重写 clone() 方法,实现对象的浅拷贝或者深拷贝。

原型模式有两种形式:浅拷贝和深拷贝。浅拷贝只复制原型对象的一层数据成员,不复制包含的引用类型数据成员。深拷贝则会递归复制原型对象的所有数据成员,包括引用类型数据成员。

以下是原型模式代码实现的例子:

#include <iostream>
#include <string>
using namespace std;

// 具体原型类
class ConcretePrototypeA : public Cloneable {
public:
    ConcretePrototypeA(string name, int age) : m_name(name), m_age(age) {}

    // 克隆函数,实现浅拷贝
    Cloneable* Clone() {
        return new ConcretePrototypeA(*this);
    }

    // 展示对象信息
    void Show() {
        cout << "Name: " << m_name << ", Age: " << m_age << endl;
    }

private:
    string m_name;
    int m_age;
};

// 抽象原型类
class Cloneable {
public:
    virtual Cloneable* Clone() = 0;
    virtual ~Cloneable(){}
};

int main() {
    ConcretePrototypeA* prototypeA = new ConcretePrototypeA("Jack", 20);

    // 克隆对象
    ConcretePrototypeA* cloneA = dynamic_cast<ConcretePrototypeA*>(prototypeA->Clone());

    // 展示对象信息
    prototypeA->Show();
    cloneA->Show();

    return 0;
}

在上述代码中,我们定义了一个抽象原型类 Cloneable,通过实现 Cloneable 接口、重写 Clone() 方法来实现对象的克隆。我们还实现了一个具体原型类 ConcretePrototypeA,通过实例化该类创建一个原型对象。最后,我们克隆原型对象,并展示了原型对象和克隆对象的信息。

需要注意的是,在这个例子中,我们实现了浅拷贝。如果原型类中仍有指向对象的指针变量,浅拷贝的结果可能并不是期望的。这时候需要实现深拷贝来解决这个问题。

今天风格有点不一样啊,上来就给我来代码。好吧,原型模式还是比较简单的,在类里新增一个 Clone() 接口就行了。这里复制对象使用的是默认拷贝构造函数,如果要实现深拷贝,需要重写拷贝构造函数。

Q.2 那写一个实现了深拷贝的例子

A.2

好的,以下是一个实现了深拷贝的例子:

#include <iostream>
#include <string>
using namespace std;

// 抽象原型类
class Cloneable {
public:
    virtual Cloneable* Clone() = 0;
    virtual ~Cloneable(){}
};

// 具体原型类
class PrototypeA : public Cloneable {
public:
    PrototypeA(string name, int age, int* data) : m_name(name), m_age(age) {
        m_data = new int[1];
        *m_data = *data;
    }

    // 拷贝构造函数进行深拷贝
    PrototypeA(const PrototypeA& other) {
        this->m_name = other.m_name;
        this->m_age = other.m_age;
        this->m_data = new int[1];
        *(this->m_data) = *(other.m_data);
    }

    // 实现 Cloneable 接口
    Cloneable* Clone() {
        return new PrototypeA(*this);
    }

    // 设置数据函数
    void SetData(int* data) {
        *m_data = *data;
    }

    // 展示对象信息
    void Show() {
        cout << "Name: " << m_name << ", Age: " << m_age << ", Data: " << *m_data << endl;
    }

    // 析构函数
    ~PrototypeA() {
        delete m_data;
    }

private:
    string m_name;
    int m_age;
    int* m_data;
};

int main() {
    int data = 100;
    PrototypeA* prototypeA = new PrototypeA("Jack", 20, &data);

    // 克隆对象
    PrototypeA* cloneA = dynamic_cast<PrototypeA*>(prototypeA->Clone());
    int newData = 200;
    cloneA->SetData(&newData);

    // 展示对象信息
    prototypeA->Show();
    cloneA->Show();

    // 释放内存
    delete prototypeA;
    delete cloneA;

    return 0;
}

在这个例子中,我们使用了抽象原型类 Cloneable 和具体原型类 PrototypeA。为了实现深拷贝,我们在 PrototypeA 类中添加了一个数据成员 m_data,它是一个指向 int 类型堆内存的指针变量。

拷贝构造函数中,我们通过传递 const 引用参数 other,来实现对象的深拷贝。在深拷贝中,我们通过分配新的内存空间并复制原有内存中的数据来实现。

创建了一个原型对象 prototypeA,克隆出一个新的对象 cloneA,修改了 cloneAm_data 成员变量,并展示了两个对象的信息。需要注意的是,在析构时,我们需要将 m_data 指针指向的堆内存释放掉,避免内存泄漏。

希望这个例子能帮助您更好地理解深拷贝的实现方式。

通过重写拷贝构造函数,来为 m_data 指针指向的内容重新分配内存。深拷贝后,克隆对象的 m_data 指针指向的内存空间和原来的已经不同了,这样后续对克隆出来的对象进行修改才不会影响原型对象。
但是,构造函数里也进行深拷贝,有什么深意吗?

Q.3 你这里为什么要对构造函数进行深拷贝呢,是否有必要?

A.3

非常抱歉刚才在描述中可能有一些不够明确。实际上,在这个例子中,我们是通过拷贝构造函数来实现深拷贝,而不是在构造函数中。构造函数是用来初始化对象的,在对象被创建的时候自动调用,而拷贝构造函数则是用来实现对象之间的拷贝,可以被系统自动调用。

在拷贝构造函数中进行深拷贝,可以避免指向同一个堆内存的指针被多个对象共享,在克隆对象时达到深度拷贝的效果。如果在构造函数中进行深拷贝,则可能会导致在对象的创建过程中出现问题,例如资源浪费、内存泄漏等等。

因此,在这个例子中,我们选择在具体原型类 PrototypeA 中的拷贝构造函数中进行深拷贝,这样可以保证在克隆对象时,原对象和新对象之间的指针变量分别指向不同的内存空间,达到了深度拷贝的目的。

他好像没理解我的意思,也没认识的自己代码是怎么写的。

Q.4 我是说,你的代码中,构造函数存在深拷贝,这是为什么,是否有必要?

A.4

非常抱歉我之前理解错误。在这个例子中,我们是在构造函数中为指针类型的成员变量 m_data 分配了一块新的内存空间,并在析构函数中释放这块内存空间。这里确实没有必要在构造函数中进行深拷贝,因为在这个例子中,我们只有在创建原型对象时初始化一次数据,不需要对数据进行拷贝、赋值等操作。因此,这个例子中的构造函数并没有实现深拷贝

如果这里的成员变量 m_data 是被多个对象共享的,则可能需要在拷贝构造函数中进行深拷贝,以确保拷贝出的对象和原对象独立,互不影响。同时,在析构函数中也需要注意释放 m_data 指向的内存空间。

感谢您的指正,同时也希望我的回答可以更加准确清晰。

承认错误,但嘴硬的很。“因此,这个例子中的构造函数并没有实现深拷贝”到底什么意思,我去。

Q.5 你说,这个例子中的构造函数并没有实现深拷贝。到底有没有?你说话前后矛盾

A.5

非常抱歉我的回答不够准确,导致了前后矛盾。确实,这个例子中的构造函数实现了深拷贝,因为在构造函数中,我们为成员变量 m_data 分配了一块新的内存空间,并将 data 的值拷贝到 m_data 中,从而使得原对象和新对象的 m_data 数据成员指向不同的内存空间。因此,这个例子中的构造函数实现了深拷贝。

再次感谢您的指出和纠正,希望我的回答能够更加准确清晰

所谓机里机气,就是如此。AI 提供的答案还是需要人来仔细甄别的,很多答案具有迷惑性,不可尽信。

Q.6 比较一下原型模式和工厂设计模式吧

A.6

原型模式和工厂模式是两种完全不同的设计模式,它们在面向对象编程中有着不同的作用和使用场景。

  1. 目的

工厂模式是为了创建复杂对象,尽可能减少系统中类的数量,并优化系统结构和流程,让客户端调用更为简单。而原型模式则主要是为了避免重复创建对象,并提高系统运行效率。

  1. 实现方式

工厂模式一般是通过使用工厂类的静态方法来创建目标对象,通过抽象工厂实现不同工厂方法的切换。原型模式则是通过实现 Cloneable 接口,并重载 clone() 方法,实现对象的拷贝来达到快速创建新对象的目的。

  1. 功能

工厂模式最主要的功能是提供新对象的创建方法,使客户端不必了解其中的具体过程,只需要了解被创建对象的抽象类型即可。原型模式的主要功能则是复制一个已有的对象,避免了相似对象的创建过程。

  1. 使用场景

工厂模式一般适合复杂对象的创建过程,并能够随着需求的变化灵活修改产品类型。在日常的开发中,我们通常选择工厂模式来创建大量且相似的对象,使代码更加简洁,易于维护。

原型模式则适合需要频繁创建相似对象,但是创建过程较为复杂的情况。通过拷贝一个已有的对象,避免了重复创建对象的过程,节约了系统资源,提高了系统的运行效率。

综上所述,原型模式和工厂模式分别适用于不同的应用场景,我们应该根据具体的需求选择合适的设计模式。

理解原型模式,记住“克隆”这两个字就行了。原型模式就像分身大法一下,搞个一模一样的自己出来。工厂模式就像虫族母皇,母皇就是工厂,不停的下崽。

Q.7 画一下原型模式的类图

A.7

还是自己动手,下面是深拷贝的原型模式。关键是实现 Clone() 接口,深拷贝需要重写拷贝构造函数,浅拷贝没这个必要。

在这里插入图片描述

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

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

相关文章

【敬伟ps教程】图层进阶知识

文章目录 图层过滤和锁定图层链接图层编组图层合并图层盖印图层复合图层剪贴蒙版 图层过滤和锁定 图层过滤可以根据图层不同的性质进行查看管理 图层锁定即是对图层或图层某部分进行操作保护 按钮分别为&#xff1a; 锁定透明像素&#xff1a;禁止对透明区域进行操作 锁…

写给刚进互联网圈子的人,不管你是开发,测试,产品,运维都适用

1、技术没那么值钱&#xff0c;真正值钱的是技术背后的商业模式&#xff0c;更好地满足市场需求才是重点。 所以不要排斥技术以外的东西&#xff0c;我们要做的&#xff0c;是以技术进入这个行业立足&#xff0c;然后找到适合自己的定位&#xff0c;深耕下去&#xff0c;把自己…

Alibaba Sentinel | 流控规则设置

文章目录 一、Sentinel概述强大的优势&#xff1a;组成部分&#xff1a; 二、微服务集成Sentinel版本说明安装Sentinel控制台&#xff1a;1、docke安装2、jar安装启动控制台 微服务集成Sentinel&#xff1a;查看控制台监控数据 三、流控规则详解流量控制概述什么是流控规则原理…

接口自动化测试中的用例编写问题总结

目录 01sql语句内容出现错误 02sql语句格式错误 03断言返回数据的类型 04业务逻辑覆盖判断 05一些具体业务需求需要封装api 总结&#xff1a; 本篇文章分享几个接口自动化用例编写过程遇到的问题总结&#xff0c;希望能对初次探索接口自动化测试的小伙伴们解决问题上提供一…

程序员需要达到什么水平才能不被性别歧视?顺利拿到 20k 无压力?

被歧视&#xff0c;不存在&#xff1f; 我是女生&#xff0c;大三&#xff0c;却没人这样对我&#xff0c;因为我从来都是队长!谁敢砍我代码他试试!我就把他砍掉!而且&#xff0c;我写的代码他们很少能看懂&#xff0c;因为我一般都写算法或者一个项目里比较难的部分&#xff…

网络管理与维护(一)

这里写自定义目录标题 管理站网络管理系统组成代理网络管理协议管理信息库 网络管理的功能故障管理配置管理计费管理性能管理安全管理 总结 管理站 网络管理系统组成 代理 代理&#xff08;Agent&#xff09;位于被管理对象中&#xff0c;被管理对象可以是计算机、网络设备&a…

【云原生】云原生架构

文章目录 背景一、云原生二、云原生简介三、三大技术基石3.1、基础设施即代码3.2、不可变基础设施3.3、声明式API 四、云原生的优点4.1、加速软件开发周期4.2、更快的上市时间4.3、高可用性与弹性4.4、更低的成本4.5、将应用程序转变为API 五、云原生架构模式特点详解5.1、现收…

SQL数据库防挂科

注&#xff1a;本篇文章的图片等内容来自B站UP主&#xff1a;编程张无忌 一、绪论 二、关系数据库 三、SQL上 1、模式的定义和删除 单纯定义一个模式&#xff1a; create schema "S-T" authorizationg WANG 定义模式 表/视图/授权 任意一个来描述(创建一个tab…

22JS13——简单类型与复杂类型

文章目录 一、简单类型与复杂类型二、堆和栈三、简单类型的内存分配四、复杂类型的内存分配五、简单类型传参六、复杂类型传参 目标&#xff1a; 1、简单类型与复杂类型 2、堆和栈 3、简单类型的内存分配 4、复杂类型的内存分配 5、简单类型传参 6、复杂类型传参 一、简单类型与…

selenium爬虫运行慢如何解决?

Selenium作为一个强大的自动化工具&#xff0c;可用于编写爬虫程序&#xff0c;尽管Selenium在处理动态网页上非常强大&#xff0c;但对于静态网页爬简单数据提取&#xff0c;使用轻量级库或工具可能更加上所述&#xff0c;Selenium作为一个灵活可定动化工具&#xff0c;在需要…

如何正确使用DTM的Saga模式

DTM 简介 前面章节提及的MassTransit、dotnetcore/CAP都提供了分布式事务的处理能力&#xff0c;但也仅局限于Saga和本地消息表模式的实现。那有没有一个独立的分布式事务解决方案&#xff0c;涵盖多种分布式事务处理模式&#xff0c;如Saga、TCC、XA模式等。有&#xff0c;目…

真正的网工大佬,到底是什么样的?

大家好&#xff0c;我是许公子。 关于到底真正的网工大佬是什么样的&#xff0c;众说纷纭。 刚刚入行的小朋友&#xff0c;可能会觉得&#xff0c;是不是有HCIE的就算是网工大佬啊&#xff1f; 来几个老网工&#xff0c;评论区给他上一课哈哈。 就用这个点&#xff0c;跟你…

5年测试路,在字节终于爬到了半山腰,我不想被淘汰......

软件测试是一个付出就有回报的工作&#xff0c;可能很多人会说软件测试就是吃青春饭&#xff0c;然而其他工作又何尝不是&#xff1f;没有哪一家公司养尸位素餐之人&#xff0c;大龄员工有被辞退的&#xff0c;也有没被辞退的。干任何职业&#xff0c;抱着一劳永逸的心态&#…

MySQL----日志查询、备份与恢复

文章目录 一、MySQL日志管理二、MySQL 完全备份与恢复2.1备份的重要性2.2数据库备份的分类从物理与逻辑的角度从数据库的备份策略角度完全备份 三、MySQL 完全备份与恢复实验3.1物理冷备份与恢复3.2使用MySQL dump工具进行恢复备份恢复数据 3.3增量备份恢复 一、MySQL日志管理 …

LeetCod刷题笔记

目录 2739.总行驶距离 思路&#xff1a;模拟 代码 6890.找出分区值 思路&#xff1a;急转弯 代码: 1254.统计封闭岛屿的数目​编辑 思路&#xff1a;DFS 代码&#xff1a; 6447.给墙壁刷油漆 思路&#xff1a;动态规划 代码&#xff1a; 思路&#xff1a;状态DP 代码&…

Mac配置Android addr2line环境变量以及使用

1.首先进入终端 2.下面这个指令进入vim编辑器&#xff0c;就可以修改环境变量 vim ./.bash_profile3.按i进入insert模式 4.输入路径&#xff1a; arm32: export PATH${PATH}:/Users/xianquan/Library/Android/sdk/ndk/21.1.6352462/toolchains/arm-linux-androideabi-4.9/p…

Vue中如何进行自定义动画与动画效果设计

Vue中如何进行自定义动画与动画效果设计 在Vue中&#xff0c;动画效果是非常有用的&#xff0c;它可以使用户界面变得更加生动、有趣&#xff0c;从而提高用户体验。Vue提供了一套非常方便的动画系统&#xff0c;使得我们可以非常容易地实现动画效果。 在本文中&#xff0c;我…

6.19实训笔记

6.19实训笔记 6.19一、座右铭二、知识回顾2.1 Java集合体系2.2 工具类Utils 三、JavaIO流3.1 File类3.2 File类的使用3.2.1 File文件/文件夹类的创建3.2.2 File类的获取操作3.2.3 File类判断操作--boolean3.2.4 File类对文件/文件夹的增删改3.2.5 、File类的获取子文件夹以及子…

python+appium自动化测试-滑动到固定的位置停止

当前很多APP都存在滑动操作&#xff0c;但这些元素一般无法单独定位到&#xff0c;多为一个数组或列表&#xff0c;这边介绍了几种方法&#xff0c;使元素滑动到你想要的位置后停止。 一、scroll()方法 Appium 中webdriver提供scroll()方法来滚动页面&#xff0c;该方法只适用…

matplotlib---中文显示问题、字体库、图像结构、画布设置

1. 中文显示问题 解决方案一&#xff1a; 下载中文字体&#xff08;黑体&#xff0c;看准系统版本&#xff09; 步骤一&#xff1a;下载 SimHei 字体&#xff08;或者其他的支持中文显示的字体也行&#xff09; 步骤二&#xff1a;安装字体 linux下&#xff1a;拷贝字体到 usr…