C++ 设计模式——备忘录模式

C++ 设计模式——备忘录模式

    • C++ 设计模式——备忘录模式
      • 1. 主要组成成分
      • 2. 逐步构建备忘录模式
        • 步骤1: 创建备忘录
        • 步骤2: 实现原发器
        • 步骤3: 创建管理者(负责人)类
        • 步骤4: 客户端使用
      • 3. 备忘录模式 UML 图
        • UML 图解析
      • 4. 备忘录模式的优点
      • 5. 备忘录模式的缺点
      • 6. 备忘录模式适用场景
      • 总结
      • 完整代码

C++ 设计模式——备忘录模式

备忘录(Memento)模式也称为快照(Snapshot)模式,是一种行为型模式,主要用于防止数据丢失。它通过对对象的状态进行备份,以便在未来需要时可以恢复这些数据。换句话说,该模式能够将某个时间点的对象内部状态保存下来,并在必要时根据保存的内容将该对象恢复到当时的状态。备忘录模式的结构比较简单,使用频率相对较低,但在特定场景下非常有用。

1. 主要组成成分

  1. 原发器(Originator): 负责创建备忘录对象以及根据备忘录恢复自身状态的类。
  2. 备忘录(Memento): 用于存储原发器的内部状态的类。
  3. 负责人/管理员(Caretaker): 负责管理备忘录的类,控制备忘录的存取,但不修改备忘录的内容。

2. 逐步构建备忘录模式

该示例代码模拟了一个游戏中的玩家角色(Fighter),它能够保存和恢复其状态(如生命值、魔法值和攻击力)。以下是每个步骤的详细说明:

步骤1: 创建备忘录

创建备忘录类,该类将保存原发器的状态。在这一步,定义一个名为 FighterMemento 的类。这个类的主要作用是存储玩家角色的状态信息。具体来说,它保存了玩家的生命值、魔法值和攻击力。通过使用私有构造函数,该类确保只能通过 Fighter 类创建备忘录,从而保护内部状态不被随意访问。

//玩家主角相关的备忘录类
class FighterMemento
{
private://构造函数,用private修饰以防止在外部被随意创建FighterMemento(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}private://提供一些供Fighter类来访问的接口,用private修饰防止被任意类访问friend class Fighter; //友元类Fighter可以访问本类的私有成员函数int getLife() const { return m_life; }void setLife(int life) { m_life = life; }int getMagic() const { return m_magic; }void setMagic(int magic) { m_magic = magic; }int getAttack() const { return m_attack; }void setAttack(int attack) { m_attack = attack; }
private://玩家主角类中要保存起来的数据,就放到这里来int m_life;    //生命值int m_magic;   //魔法值int m_attack;  //攻击力
};
步骤2: 实现原发器

定义原发器类 Fighter,它负责创建备忘录并能从备忘录中恢复状态。在此步骤中,定义了 Fighter 类,它代表游戏中的玩家角色。此类包含了角色的基本属性(生命值、魔法值和攻击力),并提供了方法来创建备忘录和从备忘录中恢复状态。通过 createMomento() 方法,当前状态将被保存到备忘录中,而 restoreMomento() 方法则用于从备忘录恢复状态。

//玩家主角类
class Fighter
{
public://构造函数Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}public://将玩家数据写入备忘录(创建备忘录,并在其中存储了当前状态)FighterMemento* createMomento(){return new FighterMemento(m_life, m_magic, m_attack);}//从备忘录中恢复玩家数据void restoreMomento(FighterMemento* pfm){m_life = pfm->getLife();m_magic = pfm->getMagic();m_attack = pfm->getAttack();}//为测试目的引入的接口,设置玩家的生命值为0(玩家死亡)void setToDead(){m_life = 0;}//用于输出一些信息void displayInfo(){cout << "玩家主角当前的生命值、魔法值、攻击力分别为:" << m_life << "," << m_magic << "," << m_attack << endl;}private://角色属性int m_life;    //生命值int m_magic;   //魔法值int m_attack;  //攻击力//......其他数据略
};
步骤3: 创建管理者(负责人)类

定义管理者类 FCareTaker,用于管理备忘录。在这一步,定义了 FCareTakerFCareTaker2 类。它们负责管理备忘录对象。FCareTaker 类能够保存单个备忘录,而 FCareTaker2 类支持多个备忘录的管理。在游戏中,这允许存储多个角色状态快照,以便在需要时进行恢复。

//管理者(负责人)类
class FCareTaker
{
public://构造函数FCareTaker(FighterMemento* ptmpfm) :m_pfm(ptmpfm) {} //形参是指向备忘录对象的指针//获取指向备忘录对象的指针FighterMemento* getMemento(){return m_pfm;}//保存指向备忘录对象的指针void setMemento(FighterMemento* ptmpfm){m_pfm = ptmpfm;}
private:FighterMemento* m_pfm; //指向备忘录对象的指针
};//-----------------
//支持多个快照的负责人(管理者)类
class FCareTaker2
{
public://析构函数用于释放资源~FCareTaker2(){for (auto iter = m_pfmContainer.begin(); iter != m_pfmContainer.end(); ++iter){delete (*iter);} //end for}//保存指向备忘录对象的指针void setMemento(FighterMemento* ptmpfm){m_pfmContainer.push_back(ptmpfm);}//获取指向备忘录对象的指针FighterMemento* getMemento(int index){auto iter = m_pfmContainer.begin();for (int i = 0; i <= index; ++i){if (i == index)return (*iter);else++iter;} //end forreturn nullptr;}
private://存储备忘录对象指针的容器vector<FighterMemento*> m_pfmContainer;  //#include <vector>
};
步骤4: 客户端使用

在客户端代码中使用备忘录模式。在客户端代码中,实例化 FighterFCareTaker2 对象,并模拟角色的状态变化。通过调用 createMomento() 方法创建备忘录,然后更改角色状态并再次创建备忘录。最后,通过 restoreMomento() 方法从备忘录恢复角色状态,并输出角色的当前状态。

int main()
{Fighter* p_fighter = new Fighter(800, 200, 300);//(1)显示玩家主角在与BOSS战斗之前的信息p_fighter->displayInfo();//(2)为玩家主角类对象创建一个备忘录对象(其中保存了当前主角类对象中的必要信息)//FighterMemento* p_fighterMemo = p_fighter->createMomento();FCareTaker* pfcaretaker = new FCareTaker(p_fighter->createMomento());//(3)玩家与BOSS开始战斗cout << "玩家主角与BOSS开始进行激烈的战斗------" << endl;p_fighter->setToDead();   //玩家主角在与BOSS战斗中,生命值最终变成0而死亡(被BOSS击败)p_fighter->displayInfo(); //显示玩家主角在与BOSS战斗之后的信息//(4)因为在与BOSS战斗之前已经通过NPC保存了游戏进度,这里模拟载入游戏进度,恢复玩家主角类对象的数据,让其可以与BOSS再次战斗cout << "玩家主角通过备忘录恢复自己的信息------" << endl;//p_fighter->restoreMomento(p_fighterMemo);p_fighter->restoreMomento(pfcaretaker->getMemento());p_fighter->displayInfo(); //显示玩家主角通过备忘录恢复到战斗之前的信息//(5)释放资源//delete p_fighterMemo;delete pfcaretaker->getMemento();delete pfcaretaker; //新增delete p_fighter;Fighter* p_fighter2 = new Fighter(800, 200, 300);FCareTaker2* pfcaretaker2 = new FCareTaker2();pfcaretaker2->setMemento(p_fighter2->createMomento()); // 第一次快照,生命值为800p_fighter2->setToDead(); // 改变玩家主角的生命值pfcaretaker2->setMemento(p_fighter2->createMomento()); // 第二次快照,生命值为0p_fighter2->displayInfo(); // 当前生命值为0cout << "------------------" << endl;// 恢复第一次快照,生命值恢复为800p_fighter2->restoreMomento(pfcaretaker2->getMemento(0));p_fighter2->displayInfo(); // 玩家主角生命值应恢复为800// 释放资源delete p_fighter2;delete pfcaretaker2;return 0;
}

3. 备忘录模式 UML 图

备忘录模式 UML 图

UML 图解析

备忘录模式的 UML 图中包含3种角色:

  • Originator (原发器):
    • 原发器是一个普通的业务类,它负责创建备忘录以保存自身的当前内部状态。后续,原发器可以使用备忘录来恢复其内部状态。原发器可以根据需要决定备忘录将存储哪些内部状态。这里指 Fighter 类实现。
  • Memento (备忘录):
    • 备忘录是一个对象,用于存储原发器在某个时刻的内部状态。备忘录的设计通常会参考原发器的设计。为了保护备忘录中的信息不被外部访问,除了创建备忘录的原发器外,其他对象不应直接使用或修改备忘录。因此,备忘录的接口一般使用 private 修饰,并将原发器类设置为友元类。这样可以避免暴露原发器管理的信息,使得备忘录成为一个被动的存储结构。这里指FighterMemento 类。
  • Caretaker (负责人/管理者):
    • 负责人负责保存备忘录,并可以将备忘录传递给其他对象,但不需要了解备忘录的具体细节,也不能对备忘录中的内容进行操作或检查。负责人的主要职责是管理备忘录的生命周期,确保其有效性和正确性。这里指 FCareTaker 类实现。

4. 备忘录模式的优点

  • 封装性: 备忘录模式将对象的状态封装在备忘录中,使得外部无法访问对象的内部状态。
  • 简化恢复操作: 通过备忘录,可以方便地恢复对象到之前的状态,而不需要了解对象的具体实现细节。
  • 历史记录管理: 可以轻松实现对象状态的历史记录功能。

5. 备忘录模式的缺点

  • 内存消耗: 如果对象状态庞大或频繁创建备忘录,可能导致内存消耗增大。
  • 状态管理复杂性: 在需要管理多个状态时,备忘录的数量可能会迅速增加,管理起来会变得复杂。

6. 备忘录模式适用场景

  • 文本编辑器: 保存文档的历史状态,以便用户能够撤销和重做操作。
  • 游戏存档: 在游戏中保存玩家的状态,以便在需要时恢复。
  • 事务处理: 在数据库操作中保存事务的状态,以支持回滚操作。

总结

备忘录模式是一种有效的设计模式,能够帮助开发者管理对象状态的保存与恢复。在需要保留对象历史状态的应用场景中,备忘录模式提供了一种高效、简单的解决方案。

完整代码

#include <iostream>
#include <vector>using namespace std;//玩家主角相关的备忘录类
class FighterMemento
{
private://构造函数,用private修饰以防止在外部被随意创建FighterMemento(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}private://提供一些供Fighter类来访问的接口,用private修饰防止被任意类访问friend class Fighter; //友元类Fighter可以访问本类的私有成员函数int getLife() const { return m_life; }void setLife(int life) { m_life = life; }int getMagic() const { return m_magic; }void setMagic(int magic) { m_magic = magic; }int getAttack() const { return m_attack; }void setAttack(int attack) { m_attack = attack; }
private://玩家主角类中要保存起来的数据,就放到这里来int m_life;    //生命值int m_magic;   //魔法值int m_attack;  //攻击力
};//玩家主角类
class Fighter
{
public://构造函数Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}public://将玩家数据写入备忘录(创建备忘录,并在其中存储了当前状态)FighterMemento* createMomento(){return new FighterMemento(m_life, m_magic, m_attack);}//从备忘录中恢复玩家数据void restoreMomento(FighterMemento* pfm){m_life = pfm->getLife();m_magic = pfm->getMagic();m_attack = pfm->getAttack();}//为测试目的引入的接口,设置玩家的生命值为0(玩家死亡)void setToDead(){m_life = 0;}//用于输出一些信息void displayInfo(){cout << "玩家主角当前的生命值、魔法值、攻击力分别为:" << m_life << "," << m_magic << "," << m_attack << endl;}private://角色属性int m_life;    //生命值int m_magic;   //魔法值int m_attack;  //攻击力//......其他数据略
};//---------------------
//管理者(负责人)类
class FCareTaker
{
public://构造函数FCareTaker(FighterMemento* ptmpfm) :m_pfm(ptmpfm) {} //形参是指向备忘录对象的指针//获取指向备忘录对象的指针FighterMemento* getMemento(){return m_pfm;}//保存指向备忘录对象的指针void setMemento(FighterMemento* ptmpfm){m_pfm = ptmpfm;}
private:FighterMemento* m_pfm; //指向备忘录对象的指针
};//-----------------
//支持多个快照的负责人(管理者)类
class FCareTaker2
{
public://析构函数用于释放资源~FCareTaker2(){for (auto iter = m_pfmContainer.begin(); iter != m_pfmContainer.end(); ++iter){delete (*iter);} //end for}//保存指向备忘录对象的指针void setMemento(FighterMemento* ptmpfm){m_pfmContainer.push_back(ptmpfm);}//获取指向备忘录对象的指针FighterMemento* getMemento(int index){auto iter = m_pfmContainer.begin();for (int i = 0; i <= index; ++i){if (i == index)return (*iter);else++iter;} //end forreturn nullptr;}
private://存储备忘录对象指针的容器vector<FighterMemento*> m_pfmContainer;  //#include <vector>
};int main()
{Fighter* p_fighter = new Fighter(800, 200, 300);//(1)显示玩家主角在与BOSS战斗之前的信息p_fighter->displayInfo();//(2)为玩家主角类对象创建一个备忘录对象(其中保存了当前主角类对象中的必要信息)//FighterMemento* p_fighterMemo = p_fighter->createMomento();FCareTaker* pfcaretaker = new FCareTaker(p_fighter->createMomento());//(3)玩家与BOSS开始战斗cout << "玩家主角与BOSS开始进行激烈的战斗------" << endl;p_fighter->setToDead();   //玩家主角在与BOSS战斗中,生命值最终变成0而死亡(被BOSS击败)p_fighter->displayInfo(); //显示玩家主角在与BOSS战斗之后的信息//(4)因为在与BOSS战斗之前已经通过NPC保存了游戏进度,这里模拟载入游戏进度,恢复玩家主角类对象的数据,让其可以与BOSS再次战斗cout << "玩家主角通过备忘录恢复自己的信息------" << endl;//p_fighter->restoreMomento(p_fighterMemo);p_fighter->restoreMomento(pfcaretaker->getMemento());p_fighter->displayInfo(); //显示玩家主角通过备忘录恢复到战斗之前的信息//(5)释放资源//delete p_fighterMemo;delete pfcaretaker->getMemento();delete pfcaretaker; //新增delete p_fighter;Fighter* p_fighter2 = new Fighter(800, 200, 300);FCareTaker2* pfcaretaker2 = new FCareTaker2();pfcaretaker2->setMemento(p_fighter2->createMomento()); // 第一次快照,生命值为800p_fighter2->setToDead(); // 改变玩家主角的生命值pfcaretaker2->setMemento(p_fighter2->createMomento()); // 第二次快照,生命值为0p_fighter2->displayInfo(); // 当前生命值为0cout << "------------------" << endl;// 恢复第一次快照,生命值恢复为800p_fighter2->restoreMomento(pfcaretaker2->getMemento(0));p_fighter2->displayInfo(); // 玩家主角生命值应恢复为800// 释放资源delete p_fighter2;delete pfcaretaker2;return 0;
}

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

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

相关文章

Camtasia 2024 v2024.0.6 for Mac 中文版 屏幕录像视频录制编辑软件

TechSmith Camtasia for Mac 中文版 是一款专业的屏幕录像及视频录制编辑软件。以业界领先的清晰度重新定义了屏幕录制&#xff0c;将屏幕、摄像头、麦克风和系统音频捕获为独立音轨&#xff0c;实现终极控制和灵活性。通过拖放过渡、标注等功能&#xff0c;以及改进的工作流程…

AI-Talk开发板之helloword

一、说明 创建第一个应用在AI-Talk开发板上运行&#xff0c;编写一个“Hello World”应用&#xff0c;启动之后在主函数里通过UART0&#xff08;debug&#xff09;打印"Hello World"。 官方指导&#xff1a;第一个应用 | 聆思文档中心 (listenai.com) 二、创建工程…

标准库标头 <optional> (C++17)学习之optional

类模板 std::optional 管理一个可选 &#xfeff;的所含值&#xff0c;即既可以存在也可以不存在的值。 一种常见的 optional 使用情况是作为可能失败的函数的返回值。与如 std::pair<T, bool> 等其他手段相比&#xff0c;optional 可以很好地处理构造开销高昂的对象&a…

Ollama拉起本地模型以及rag系统部署。

什么是 Ollama &#xff1f; Ollama 是一个简明易用的本地大模型运行框架。能在本地启动并运行 Llama、qwen、Gemma 及其他大语言模型&#xff0c;没有GPU资源照样可以拉起模型&#xff0c;和LocalAI 比较类似&#xff0c;但是加载模型更容易。 1.安装 安装后运行&#xff0c…

统一NLP和目标检测的DETR(一)——self attention、encoder、decoder

主流目标检测算法劣势 YOLO系列&#xff0c;它基于anchor来做&#xff0c;少不了要用MNS&#xff0c;导致速度相对较慢。 但今天介绍一款DETR&#xff0c;基于VIT的目标检测算法。 那么我们需要先深入过一遍VIT。 Vision transform 1、传统RNN网络的问题 单向&#xff1a;只…

YOLOv8改进实战 | 引入混合局部通道注意力模块MLCA(2023轻量级)

YOLOv8专栏导航:点击此处跳转 前言 YOLOv8 是由 YOLOv5 的发布者 Ultralytics 发布的最新版本的 YOLO。它可用于对象检测、分割、分类任务以及大型数据集的学习,并且可以在包括 CPU 和 GPU 在内的各种硬件上执行。 YOLOv8 是一种尖端的、最先进的 (SOTA) 模型,它建立在以前…

C++和Python混合编程——Python调用C++入门

大纲 背景代码入门环境准备头文件业务代码注册BOOST_PYTHON_MODULE作用 boost::python::class_模板类 编译运行项目地址 背景 Python语言在大数据、人工智能以及大模型开发中扮演着举足轻重的角色&#xff0c;其影响力不断扩大并深化。 在大数据领域&#xff0c;Python以其简…

unittest | 使用unittest模块来测试logging日志模块功能

我们在这篇文章实现了在项目工程中编写一个logging模块&#xff0c;但是我们如何确定我们编写的模块功能的是否正常? 你可能想到将全部代码写完后运行测试&#xff0c;但这是一个非常不好的习惯。❌ 最好的方式&#xff0c;是每写出来一个功能或者方法就对它进行测试&#x…

Java设计模式之装饰器模式详细讲解和案例示范

1. 引言 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许向现有对象添加新的功能&#xff0c;而无需修改其结构。这种模式通过使用组合而非继承来扩展对象的行为&#xff0c;在许多实际应用中极为常见。本文将详细介绍装饰器模式…

使用 docker 部署 kvm 图形化管理工具 WebVirtMgr

文章目录 [toc]前提条件镜像构建启动 webvirtmgr创建其他 superuser配置 nginx 反向代理和域名访问绑定 kvm 宿主机local sockettcp 连接 虚拟机创建创建快照虚拟机克隆删除虚拟机 kvm 官方提供了以下这些图形化管理&#xff0c;license 这块也提示了是商业版&#xff08;Comme…

Android实习面经整理第一篇

蔚来Android实习面经 一面(2024/3/11 35min) 自我介绍聊我的本专业说一说MVP架构,MVVM架构 MVP:V层持有P层,用户点击View,把数据发给P层,P层持有M层,然后P层把V层的数据发给M层获取其他数据,最后M层获取完数据后把数据还给P层,更新V层。P层也有V层的引用。MVVM:V层…

Qt 去掉QDialog对话框的问号

QT 对话框的问号是什么&#xff1f; QDialog默认的window flag中包含了Qt::WindowContextHelpButtonHint,这个flag意思是在窗口上提供“上下文帮助”按钮 使用方式/调用方式 void QWidget::setWhatsThis(const QString &)比如&#xff1a; ui->lineEdit_1->setWh…

【pycharm-乱码】简单记录一下都有哪些涉及编码

控制台 路径&#xff1a;setting-》general-》console setting-》editor-》file encodings 路径&#xff1a;setting-》editor->file and code templates #!/user/bin/env python3 # -*- coding: utf-8 -*-setting->tools->ssh terminal

Conda在线/离线迁移虚拟环境

conda简单使用 1.创建环境&#xff1a; conda create -n myenv python3.82.激活环境 conda activate myenv3.退出环境 conda deactivate4.安装包 pip install xxx5.列出所有环境 conda env list conda info --envs6.删除环境 conda remove -n myenv --all离线迁移conda …

【JavaWeb】JDBCDruidTomcat入门使用

本章使用技术版本&#xff1a; Tomcatv10.1.25 关于javaweb相关的其他技术&#xff0c;比如tomcat和maven&#xff0c;在我的主页记录了笔记&#xff0c;ajax我用的是本地笔记以后再考虑上传&#xff0c;前端三板斧我用的菜鸟教程文档 JDBC 初识 JDBC概念 JDBC 就是使用Jav…

三、建造者模式

构造者模式&#xff08;Builder Pattern&#xff09;使用简单的对象一步一步构建成一个复杂的对象。这种设计模式属于创建者模式&#xff0c;它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。例如&#xff0c;计算…

LVDS与SerDes到底是什么关系?

随着智能座舱和智能驾驶功能的应用&#xff0c;汽车中的摄像头和液晶屏越来越多&#xff0c;多种图像显示和多屏互动也成为增强汽车智能化和用户体验的重点内容。 这些图像显示功能需要在不同的控制器之间或者控制器与远端的液晶屏或摄像头之间进行数据传输。 比如中控与仪表之…

AndroidLogger 适配好了,但没法上架

看到有网友还在用之前的 AndroidLogger 版本&#xff0c;让我感动再次花了 2个月适配新的Notepad&#xff0c;总算搞完了&#xff0c;但是Notepad作者反了&#xff0c;我没法上架啊。 演示视频地址&#xff1a; Notepad安卓日志插件&#xff0c;支持文件管理和截屏&#xff0c…

FreeRTOS学习笔记—②RTOS的认识(持续更新中)

由于正在学习韦东山大佬的RTOS课程&#xff0c;结合了网上的一些资料&#xff0c;整理记录了下自己的感悟&#xff0c;用于以后自己的回顾。如有不对的地方请各位大佬纠正。 课程链接&#xff1a;https://www.bilibili.com/video/BV1844y1g7ud/?spm_id_from333.337.search-car…

Elasticsearch 向量数据库本地部署 及操作方法

elasticsearch是个分布式向量数据库&#xff0c;支持多种查找模式。此外还拥有 Metadata、Filtering、Hybrid Search、Delete、Store Documents、Async等能力。本文仅是记录本地测试途中遇到的问题。 一&#xff0c;环境部署 下载软件 首先去官网&#xff0c;选择适合平台下…