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

​【C++设计模式】第十八篇:备忘录模式(Memento)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

对象状态快照与回溯


1. 模式定义与用途

核心思想

  • 备忘录模式:在不破坏对象封装的前提下,捕获其内部状态快照,并在需要时恢复状态
  • 关键用途
    1.​撤销/重做操作:支持用户回退到之前的操作状态。
    ​2.状态持久化:保存对象状态至内存或磁盘(如游戏存档)。
    ​3.事务回滚:确保操作失败时能恢复至一致状态。

经典场景

  • 文本编辑器的撤销功能。
  • 游戏角色存档与读档。
  • 数据库事务的原子性保证。

2. 模式结构解析

UML类图

+---------------------+           +---------------------+  
|      Originator     |           |       Memento       |  
+---------------------+           +---------------------+  
| - state: State      |<>-------->| - state: State      |  
+----------------------+          +---------------------+  
| + createMemento()    |          | + getState()        |  
| + restore(m: Memento)|          | + setState()        |  
+----------------------+          +---------------------+  
                                          ^  
                                          |  
                                  +-------+-------+  
                                  |               |  
                          +------------------+ +----------------+  
                          |    Caretaker     | |   Client       |  
                          +------------------+ +----------------+  
                          | - mementos: list | | 触发保存与恢复  |  
                          +------------------+ +----------------+  

角色说明

  1. Originator:原发器,生成状态快照(createMemento)并从快照恢复(restore)。
  2. ​Memento:备忘录,存储原发器的内部状态(仅允许原发器访问)。
  3. Caretaker:管理者,保存备忘录历史,但不修改其内容。

3. 现代C++实现示例

场景:文本编辑器撤销功能

​步骤1:定义备忘录类(严格封装)​
#include <iostream>  
#include <string>  
#include <vector>  
#include <memory>  

// 前向声明  
class TextEditor;  

// 备忘录(仅允许TextEditor访问私有成员)  
class TextMemento {  
public:  
    friend class TextEditor;  // 原发器为友元类  

    // 仅提供读取状态的公开接口  
    const std::string& getText() const { return text_; }  

private:  
    TextMemento(const std::string& text) : text_(text) {}  
    void setText(const std::string& text) { text_ = text; }  

    std::string text_;  
};  
步骤2:实现原发器(文本编辑器)​
class TextEditor {  
public:  
    // 保存状态  
    std::shared_ptr<TextMemento> createMemento() const {  
        return std::make_shared<TextMemento>(text_);  
    }  

    // 恢复状态  
    void restore(const std::shared_ptr<TextMemento>& memento) {  
        text_ = memento->getText();  
    }  

    // 编辑操作  
    void append(const std::string& str) {  
        text_ += str;  
    }  

    void print() const {  
        std::cout << "当前文本: " << text_ << "\n";  
    }  

private:  
    std::string text_;  
};  
步骤3:实现管理者(历史记录)
class History {  
public:  
    void save(const std::shared_ptr<TextMemento>& memento) {  
        history_.push_back(memento);  
    }  

    std::shared_ptr<TextMemento> undo() {  
        if (history_.empty()) return nullptr;  
        auto last = history_.back();  
        history_.pop_back();  
        return last;  
    }  

private:  
    std::vector<std::shared_ptr<TextMemento>> history_;  
};  
步骤4:客户端代码
int main() {  
    TextEditor editor;  
    History history;  

    // 编辑并保存状态  
    editor.append("Hello");  
    history.save(editor.createMemento());  
    editor.print();  // 输出:当前文本: Hello  

    editor.append(" World");  
    history.save(editor.createMemento());  
    editor.print();  // 输出:当前文本: Hello World  

    // 撤销到上一个状态  
    auto memento = history.undo();  
    if (memento) {  
        editor.restore(memento);  
        editor.print();  // 输出:当前文本: Hello  
    }  
}  

4. 应用场景示例

场景1:游戏角色存档

class GameCharacter {  
public:  
    struct State { int health; float x, y; };  

    std::shared_ptr<Memento> save() {  
        return std::make_shared<CharacterMemento>(state_);  
    }  

    void restore(const std::shared_ptr<Memento>& memento) {  
        auto cm = std::dynamic_pointer_cast<CharacterMemento>(memento);  
        state_ = cm->getState();  
    }  

private:  
    State state_;  

    class CharacterMemento : public Memento {  
    public:  
        CharacterMemento(const State& state) : state_(state) {}  
        State getState() const { return state_; }  
    private:  
        State state_;  
    };  
};  

场景2:数据库事务回滚

class DatabaseTransaction {  
public:  
    void commit() { /* 提交事务 */ }  
    std::shared_ptr<Memento> saveCheckpoint() {  
        return std::make_shared<Checkpoint>(data_);  
    }  
    void rollback(const std::shared_ptr<Memento>& memento) {  
        data_ = memento->getData();  
    }  

private:  
    std::string data_;  

    class Checkpoint : public Memento {  
    public:  
        Checkpoint(const std::string& data) : data_(data) {}  
        std::string getData() const { return data_; }  
    private:  
        std::string data_;  
    };  
};  

5. 优缺点分析(表格形式)

​优点​缺点
状态保存与恢复逻辑解耦频繁保存大对象可能导致内存占用高
严格封装,不破坏原发器内部实现需要深拷贝,复杂对象复制成本高
支持多级撤销与历史管理Caretaker需维护大量备忘录对象

6. 调试与优化策略

调试技巧(VS2022)​

1. ​跟踪备忘录状态:
  • 在createMemento()和restore()中设置断点,验证保存和恢复的数据一致性。
2. ​检查深拷贝正确性:
  • 确保备忘录与原发器的状态完全独立,修改原发器不影响已保存的备忘录。

性能优化

1.​ 增量保存:
  • 仅保存状态的变化部分(如差异快照),而非完整状态。
class DiffMemento : public Memento {  
public:  
    DiffMemento(const std::string& diff) : diff_(diff) {}  
    std::string apply(const std::string& base) { return base + diff_; }  
private:  
    std::string diff_;  
};  
2. 懒加载备忘录:
  • 仅在需要恢复时生成快照,结合持久化存储(如磁盘缓存)。

http://www.kler.cn/a/580720.html

相关文章:

  • C# OPC DA获取DCS数据(提前配置DCOM)
  • 高级java每日一道面试题-2025年2月17日-数据库篇-使用 MySQL 的索引应该注意些什么?
  • 从0开始的操作系统手搓教程43——实现一个简单的shell
  • HTML左右分页2【搬代码】
  • NO.34十六届蓝桥杯备战函数十道练习|max|min|素数|完全数|素数对|素数回文数|真素数(C++)
  • fastapi+mysql实现增删改查
  • Flink深入浅出之04:时间、水印、TableSQL
  • 算法与数据结构(回文数)
  • 网易邮箱如何用大数据任务调度实现海量邮件数据处理?Apache DolphinScheduler用户交流会上来揭秘!
  • SpringMVC项目中,涉及到的各种请求
  • element-ui descriptions 组件源码分享
  • 多方安全计算(MPC)电子拍卖系统
  • 防火墙IPSec (无固定IP地址---一对多)
  • Redis- 大key
  • RK3588部署YOLOv8(2):OpenCV和RGA实现模型前处理对比
  • Linux权限维持之vim python 扩展后门(五)
  • Spring 中事务的实现
  • 推荐一款好用在线免费软件工具箱-传道软件箱
  • 框架基本知识总结 Day16
  • 垃圾收集算法与收集器