设计模式之备忘录模式

备忘录模式(Memento Pattern)听起来名字挺高级,其实说白了就是"后悔药"或者"存档功能"。

场景:文本编辑器的撤销(Undo)功能

我们模拟用户在编辑器里**"输入文字""删除文字"**两个动作。


❌ 不使用备忘录模式(代码混乱、破坏封装)

这种做法通常会将编辑器的内部状态直接暴露给外部管理器,或者由管理器强行持有编辑器的引用进行操作。

复制代码
#include <iostream>
#include <string>
#include <vector>

// 编辑器类
class SimpleEditor {
public:
    std::string content; // 必须公开,否则外部存不了
    void show() { std::cout << "当前内容: " << content << std::endl; }
};

int main() {
    SimpleEditor editor;
    
    // 外部管理器需要自己记录所有的历史快照
    std::vector<std::string> undoStack;

    // 动作1:输入
    undoStack.push_back(editor.content); // 备份旧状态
    editor.content += "Hello";
    
    // 动作2:再输入
    undoStack.push_back(editor.content); // 备份旧状态
    editor.content += " World";

    editor.show(); // 输出: Hello World

    // 撤销:外部管理器直接修改编辑器的私有数据
    if (!undoStack.empty()) {
        editor.content = undoStack.back();
        undoStack.pop_back();
    }

    std::cout << "--- 撤销后 ---" << std::endl;
    editor.show(); // 输出: Hello

    return 0;
}
/* 缺点:
1. 内存浪费:每次都存整个字符串,如果文档 100MB,存 10 次就 1GB 了。
2. 封装破坏:编辑器必须把 content 设为 public,任何外部代码都能改它,不安全。
*/

✅ 使用备忘录模式(增量存储、保护隐私)

这里我们使用**增量(Delta)**思想:备忘录只存"变动的部分",并且只有编辑器自己能读懂备忘录。

复制代码
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <memory>

// 1. 【备忘录类】:只存"变化"的小包
class TextMemento {
private:
    friend class ProfessionalEditor; // 只有编辑器能访问
    int pos;
    std::string text;
    bool isAddAction; // true为增加,false为删除

    TextMemento(int p, std::string t, bool isAdd) 
        : pos(p), text(t), isAddAction(isAdd) {}
};

// 2. 【原发器】:编辑器本体
class ProfessionalEditor {
private:
    std::string content;

public:
    // 输入文字,返回一个"撤销包"
    std::unique_ptr<TextMemento> type(int pos, std::string t) {
        content.insert(pos, t);
        // 记录:我在 pos 增加了 t,撤销时请删掉它
        return std::unique_ptr<TextMemento>(new TextMemento(pos, t, true));
    }

    // 删除文字,返回一个"撤销包"
    std::unique_ptr<TextMemento> erase(int pos, int len) {
        std::string deletedText = content.substr(pos, len);
        content.erase(pos, len);
        // 记录:我在 pos 删了 t,撤销时请插回去
        return std::unique_ptr<TextMemento>(new TextMemento(pos, deletedText, false));
    }

    // 【核心】:根据备忘录"反向操作"实现撤销
    void undo(const TextMemento& m) {
        if (m.isAddAction) {
            content.erase(m.pos, m.text.length()); // 撤销增加 = 删除
        } else {
            content.insert(m.pos, m.text);        // 撤销删除 = 插回
        }
    }

    void show() { std::cout << "当前内容: " << content << std::endl; }
};

// 3. 【负责人】:只管存包,不看包
class UndoManager {
private:
    std::stack<std::unique_ptr<TextMemento>> history;
public:
    void save(std::unique_ptr<TextMemento> m) { history.push(std::move(m)); }
    
    void undo(ProfessionalEditor& editor) {
        if (history.empty()) return;
        editor.undo(*history.top());
        history.pop();
    }
};

int main() {
    ProfessionalEditor editor;
    UndoManager undoManager;

    // 步骤1:输入 Hello
    undoManager.save(editor.type(0, "Hello"));
    
    // 步骤2:输入 World
    undoManager.save(editor.type(5, " World"));
    editor.show(); // Hello World

    // 步骤3:删除 World
    undoManager.save(editor.erase(5, 6));
    editor.show(); // Hello

    std::cout << "\n--- 执行连续撤销 ---\n" << std::endl;

    undoManager.undo(editor); // 撤销删除,World 回来了
    editor.show();

    undoManager.undo(editor); // 撤销输入,World 没了
    editor.show();

    return 0;
}

3. 为什么模式版更好?(优缺点总结)

优点:

  1. 极度省空间: 备忘录里只存了变动的几个字母,而不需要把整个文档复制一份。这在处理大型文档(如 Word、代码文件)时是唯一可行的方案。

  2. 职责清晰: * 编辑器知道怎么"反转"操作。

    • 管理器只负责按顺序堆叠。

    • 备忘录就是一个被保护的私有数据包。

缺点:

  1. 逻辑复杂度: 你必须为每一种操作(比如变粗体、改颜色)写好精确的"反转逻辑"。如果反转逻辑写错了(比如坐标算错),撤销就会导致文档乱码。

  2. 不能随机跳跃: 增量备忘录必须严格按 3 -> 2 -> 1 的顺序撤销,不能直接跳过 3 去撤销 2,否则数据会错位。

相关推荐
驴儿响叮当20102 小时前
设计模式之迭代器模式
设计模式·迭代器模式
qq_401700412 小时前
嵌入式C语言设计模式
c语言·开发语言·设计模式
SuperEugene2 小时前
常见设计模式在 JS 里的轻量用法:单例、发布订阅、策略
前端·javascript·设计模式·面试
小米4962 小时前
Js设计模式---策略模式
设计模式·策略模式
geovindu2 小时前
python: Strategy Pattern
python·设计模式·策略模式
sg_knight19 小时前
适配器模式(Adapter)
python·设计模式·适配器模式·adapter
郝学胜-神的一滴1 天前
Effective Modern C++ 条款40:深入理解 Atomic 与 Volatile 的多线程语义
开发语言·c++·学习·算法·设计模式·架构
九狼1 天前
Riverpod 2.0 代码生成与依赖注入
flutter·设计模式·github
geovindu1 天前
python: Visitor Pattern
python·设计模式·访问者模式