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

发布于:2025-03-11 ⋅ 阅读:(17) ⋅ 点赞:(0)

注意:复现代码时,确保 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. 懒加载备忘录:
  • 仅在需要恢复时生成快照,结合持久化存储(如磁盘缓存)。