前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++设计模式 - 备忘录模式

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

作者头像
开源519
发布2022-12-01 16:07:03
2140
发布2022-12-01 16:07:03
举报
文章被收录于专栏:开源519开源519

前言

所有通过捷径所获取到的快乐,无论是金钱、性还是名望,最终都会给自己带来痛苦。人其实很难抵制诱惑,只能远离诱惑,所以千万不要高看自己的定力。 -- 罗翔

备忘录模式

❝在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。 ❞

就是实际的回退功能,将不同时间段的各状态数据依次存储至列表。当需要回退时,从列表取出各状态值载入即可。该模式又叫快照模式。

意义

「备忘录模式」 有点类似后悔药的功能,软件回档和撤回的功能就是备忘录的体现。

应用场景

玩游戏时,人物的状态(等级和血量)是随时间和操作改变的,设计存档和回退功能,实现某个时间点人物的状态备份,并能够回退上一个备份的状态。

分析

上述场景,主要需要保存当前人物的血量和等级并存入备忘录即可,功能比较简单。

类图

备忘录类图.png

  • Coriginator: 源数据类。备份数据来源。
  • CMemento: 备忘录类。主要用于存储一份源数据。
  • CCaretaker: 备忘录管理类。内部持有备忘录表,负责备忘录表的管理,实现备份和回退功能。

源码实现

「编程环境」

  1. 编译环境: Linux环境
  2. 语言: C++语言
  3. 编译命令: make

「工程结构」

代码语言:javascript
复制
Memento/
├── caretaker.cc
├── caretaker.h
├── main.cc
├── Makefile
├── memento.cc
├── memento.h
├── originator.cc
└── originator.h
  • caretaker: 备忘录管理者代码实现
  • memento: 备忘录代码实现
  • originator: 源数据代码实现
  • main.cc: 客户端代码,程序入口
  • Makefile: 编译工具

「备忘录接口」

代码语言:javascript
复制
class CMemento
{
public:
    explicit CMemento(void *pInfo, int size);
    ~CMemento();
    int GetInfo(void *info, int size);

private:
    std::string date;
    void *mpInfo;
};

mpInfo: 在CMemento构造函数分配一块内存用于存储源数据状态值, mpInfo指向这块内存。

「源数据接口」

代码语言:javascript
复制
typedef struct
{
    int   level;
    float blood;
    char  date[64];
} SAttrValue;

class COriginator
{
public:
    COriginator();
    ~COriginator();
    CMemento* Save();
    void Restore(CMemento *pMemento);
    void SetAttribute(SAttrValue *pAttr);
    void ShowInfo();

private:
    SAttrValue mAttrValue;
};
  • 从上述接口看出主要需要保存的数据是人物的等级、血量和当前系统时间。
  • Restore接口用于从Memento中重载属性数据。
  • SetAttribute接口用于保存当前的属性数据。

「管理者接口」

代码语言:javascript
复制
class CCaretaker
{
public:
    explicit CCaretaker(COriginator *pOri);
    ~CCaretaker();
    void Backup();
    void Undo();

private:
    COriginator *pmOriginator;
    std::vector<CMemento*> mMementoArray;
};
  • CCaretaker持有Originator和Memento表,此类存在的意义在于负责对外提供Originator的备份与重载接口。从而保证Originator的类只负责存储的数据,Caretaker负责数据管理业务逻辑。
  • CCaretaker只负责Originator备份与重载,而不能直接修改Originator内部某个数据。在设计阶段要杜绝这种可能。

「客户端代码」

代码语言:javascript
复制
int main(int argc, char *argv[])
{
    COriginator theOriginator;
    CCaretaker theCCaretaker(&theOriginator);

    MAIN_LOG("---- Backup 1th attribute ----\n");
    SAttrValue attrValue = {10, 100, {0}};
    theOriginator.SetAttribute(&attrValue);
    theCCaretaker.Backup();
    theOriginator.ShowInfo();
    MAIN_LOG("------------------------------\n\n");
    sleep(3);

    MAIN_LOG("---- Backup 2th attribute ----\n");
    attrValue.level = 66;
    attrValue.blood = 80;
    theOriginator.SetAttribute(&attrValue);
    theCCaretaker.Backup();
    theOriginator.ShowInfo();
    MAIN_LOG("------------------------------\n\n");
    sleep(5);

    MAIN_LOG("----- Current attribute  -----\n");
    attrValue.level = 88;
    attrValue.blood = 60;
    theOriginator.SetAttribute(&attrValue);
    theOriginator.ShowInfo();
    MAIN_LOG("------------------------------\n\n");
    sleep(4);

    MAIN_LOG("--> Wating roll back to the previous version\n");
    sleep(2);
    theCCaretaker.Undo();
    theOriginator.ShowInfo();
    MAIN_LOG("------------------------------\n\n");

    MAIN_LOG("--> Wating roll back to the previous version\n");
    sleep(2);
    theCCaretaker.Undo();
    theOriginator.ShowInfo();
    MAIN_LOG("------------------------------\n\n");

    return 0;
}

客户端业务是备份两次,回退两次。

测试效果

代码语言:javascript
复制
$ ./exe 
---- Backup 1th attribute ----
Level: 10
Blood: 100%
Backup time: 2022-04-30 18:07:26
------------------------------

---- Backup 2th attribute ----
Level: 66
Blood: 80%
Backup time: 2022-04-30 18:07:29
------------------------------

----- Current attribute  -----
Level: 88
Blood: 60%
------------------------------

--> Wating roll back to the previous version
Level: 66
Blood: 80%
Backup time: 2022-04-30 18:07:29
------------------------------

--> Wating roll back to the previous version
Level: 10
Blood: 100%
Backup time: 2022-04-30 18:07:26
------------------------------

总结

  • 该模式提供一种可以回退上次状态的机制。能够使用户比较方便恢复历史的某个状态。
  • 但是也要注意除了源数据对象,其他对象都不应该存在直接修改源数据类成员的能力。
  • 在使用此模式时,如果过度的备份,会导致大量内存被占用。因此我们可以设计一个阈值机制,当达到阈值,抛弃备忘录最原始的版本。这么一看备忘录设计成栈结构比较合适,先进后出。
  • 「备忘录模式」的实现相对简单,也不唯一,在满足备忘录模式思想的情况下,实现方式合理即可。
  • 另外,这么多的设计模式,不要纠结于该使用哪种,结合具体场景可选择一个或多个设计模式都是可行的。多数情况下,存在多种设计模式相互配合完成某种组件或场景的实现。

最后

用心感悟,认真记录,写好每一篇文章,分享每一框干货。

更多文章内容包括但不限于C/C++、Linux、开发常用神器等,可进入“开源519公众号”聊天界面输入“文章目录” 或者 菜单栏选择“文章目录”查看。公众号后台聊天框输入本文标题,在线查看源码。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源519 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 备忘录模式
    • 意义
      • 应用场景
        • 分析
          • 类图
            • 源码实现
              • 测试效果
                • 总结
                  • 最后
                  相关产品与服务
                  对象存储
                  对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档