当前位置: 首页 > news >正文

C++ 设计模式——原型模式

原型模式

    • 原型模式
      • 主要组成部分
      • 原型模式的使用步骤
      • 原型模式的 UML 图
      • 原型模式 UML 图解析
      • 优点和缺点
      • 适用场景
      • 总结

原型模式

原型(Prototype)模式是一种创建型模式。原型模式通过(原型对象)克隆出对个一模一样的对象。实际上,该模式与其说是一种设计模式,不如说是一种创建对象的方法(对象克隆),尤其是创建给定类的对象(实例)过程很复杂(例如,要设置许多成员变量的值)时,适用这种设计模式就比较合适。

引入“原型”(Prototype)模式的定义:用原型实例指定创建对象的类型,并且通过复制这些原型创建新的对象。简单来说,就是通过克隆来创建新的对象实例。

主要组成部分

  • 原型接口 (Prototype Interface):定义一个克隆方法,通常是 clone(),用于复制当前对象的实例。
  • 具体原型类 (Concrete Prototype):实现原型接口,提供具体的克隆实现。每个具体原型类都可以被克隆以创建新对象。
  • 客户端 (Client):使用原型对象来创建新对象。客户端通过调用原型的克隆方法来获得新对象,而不是直接使用构造函数。

原型模式的使用步骤

  1. 定义原型接口

    创建一个包含克隆方法的抽象类 Monster

    //怪物父类
    class Monster
    {
    public://构造函数Monster(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}virtual ~Monster() {} //做父类时析构函数应该为虚函数public:virtual Monster* clone() = 0; //具体的实现在子类中进行protected: //可能被子类访问的成员,用protected修饰//怪物属性int m_life;    //生命值int m_magic;   //魔法值int m_attack;  //攻击力
    };
    
  2. 实现具体原型类

    创建具体的怪物类,继承自 Monster,并实现 clone() 方法。

    //亡灵类怪物
    class M_Undead :public Monster
    {
    public://构造函数M_Undead(int life, int magic, int attack) :Monster(life, magic, attack){cout << "一只亡灵类怪物来到了这个世界" << endl;}public://拷贝构造函数M_Undead(const M_Undead& tmpobj) :Monster(tmpobj){cout << "调用了M_Undead::M_Undead(const M_Undead& tmpobj)拷贝构造函数创建了一只亡灵类怪物" << endl;}virtual Monster* clone(){return new M_Undead(*this); //触发拷贝构造函数的调用来创建亡灵类怪物}//其他代码略....
    };//元素类怪物
    class M_Element :public Monster
    {
    public://构造函数M_Element(int life, int magic, int attack) :Monster(life, magic, attack){cout << "一只元素类怪物来到了这个世界" << endl;}public://拷贝构造函数M_Element(const M_Element& tmpobj) :Monster(tmpobj) //初始化列表中注意对父类子对象的初始化{cout << "调用了M_Element::M_Element(const M_Element& tmpobj)拷贝构造函数创建了一只元素类怪物" << endl;}virtual Monster* clone(){return new M_Element(*this);}//其他代码略....
    };//机械类怪物
    class M_Mechanic :public Monster
    {
    public://构造函数M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack){cout << "一只机械类怪物来到了这个世界" << endl;}public://拷贝构造函数M_Mechanic(const M_Mechanic& tmpobj) :Monster(tmpobj){cout << "调用了M_Mechanic::M_Mechanic(const M_Mechanic& tmpobj)拷贝构造函数创建了一只机械类怪物" << endl;}virtual Monster* clone(){return new M_Mechanic(*this);}//其他代码略....
    };
    
  3. 使用客户端

    在客户端代码中,创建原型对象并使用克隆方法生成新对象。

    int main()
    {// 创建原型对象Monster* undead = new M_Undead(100, 50, 20);Monster* element = new M_Element(80, 60, 25);Monster* mechanic = new M_Mechanic(120, 30, 30);// 克隆对象Monster* clonedUndead = undead->clone();Monster* clonedElement = element->clone();Monster* clonedMechanic = mechanic->clone();// 使用克隆对象...// 例如,可以在这里对克隆对象进行操作或输出属性// 清理内存delete undead;delete element;delete mechanic;delete clonedUndead;delete clonedElement;delete clonedMechanic;return 0;
    }
    

原型模式的 UML 图

原型模式的 UML 图

原型模式 UML 图解析

  • 类关系
    • Prototype(原型类):这是一个抽象类,定义了克隆接口(通常是 clone() 方法)。在 UML 图中,对应于 Monster 类。
    • ConcretePrototype(具体原型类):这些是实现了 Prototype 接口的具体类,负责实现克隆逻辑。图中的 M_UndeadM_ElementM_Mechanic 是具体原型类。
  • 稳定与变化
    • 稳定部分Prototype 类(如 Monster)通常保持不变,定义了基本的属性和克隆接口。
    • 变化部分ConcretePrototype 类(如 M_UndeadM_Element 等)是变化的部分,可以根据需求添加新的具体类。
  • 信息隐藏
    • 客户端代码与 Prototype 接口交互,而不需要了解具体的实现细节。这种设计实现了信息隐藏,使得系统更加灵活。
  • 扩展性
    • 新的具体原型类可以通过实现 Prototype 接口来扩展,而不需要修改现有的代码结构,符合开闭原则。

优点和缺点

优点

  • 简化对象创建:通过克隆现有对象,避免了复杂的构造过程,尤其是当对象需要大量初始化参数时。
  • 提高灵活性:客户端不需要了解具体的对象创建逻辑,只需使用原型进行克隆,增强了代码的灵活性和可维护性。
  • 支持动态配置:可以在运行时决定克隆哪种类型的对象,适应性强。

缺点

  • 性能开销:克隆对象可能导致较大的内存开销,特别是在对象较大或复杂时。
  • 深拷贝与浅拷贝:需要明确区分深拷贝和浅拷贝,错误的实现可能导致共享状态问题。
  • 增加复杂性:需要实现克隆方法,可能会增加代码的复杂性,特别是在有多个子类时。

适用场景

  • 对象创建过程复杂:当对象的构造过程非常复杂,涉及多个参数初始化时,使用原型模式可以简化这一过程。
  • 需要大量相似对象:当需要创建大量相似对象时,使用克隆原型可以提高效率。
  • 运行时动态创建:当对象类型在运行时动态决定时,原型模式可以提供灵活的解决方案。
  • 避免构造函数的重复调用:在需要频繁创建相似对象的场景中,原型模式可以避免重复调用构造函数,提高性能。

总结

原型模式通过定义 PrototypeConcretePrototype 的关系,使得对象的克隆过程更加灵活和高效。客户端只需依赖于 Prototype 接口,能够动态创建新的对象实例,增强了系统的可扩展性和可维护性。

http://www.lryc.cn/news/426793.html

相关文章:

  • 【Harmony OS 4.0】待办列表案例
  • 快速把文件名统计到excel表的方法
  • 开源通用验证码识别OCR —— DdddOcr 源码赏析(一)
  • 上升ECMAScript性能优化技巧与陷阱(下)
  • 用7EPhone云手机进行TikTok的矩阵运营
  • 谷歌浏览器下载文件被阻止怎么解除
  • apt E: 无法定位软件包 winehq-stable
  • P2460[SDOI2007] 科比的比赛
  • linux学习--第二天
  • 使用 Flask、Celery 和 Python 实现每月定时任务
  • 【c语言】整数在内存中的储存(大小端字节序)
  • 浅谈SIMD、向量化处理及其在StarRocks中的应用
  • 【ML】Image Augmentation)的作用、使用方法及其分类
  • 设计模式六大原则(一)--单一职责原则
  • c语言学习,malloc()函数分析
  • 【运维项目经历|041】上云项目-物理机迁移到阿里云
  • 分组并合并其它列的非空值 --Excel难题#83
  • VM相关配置及docker
  • Redis中Set数据类型常用命令
  • mysql误删数据恢复记录
  • 论文阅读:Real-time Controllable Denoising for Image and Video
  • 【Kubernetes】虚拟 IP 与 Service 的代理模式
  • 深度学习·Pytorch
  • fastzdp_sqlmodel新增get_first和is_exitsts方法
  • 嵌入式软件--数电基础 DAY 3
  • 【生成式人工智能-十五-经典的影像生成方法-GAN】
  • python 已知x+y=8 求x*y*(x-y)的最大值
  • windows平台的postgresql主从数据库流备份
  • Spring 常见设计模式
  • 优化大量数据导出到Excel的内存消耗(二):如果数据超出Excel单表上限,则进行分表