备忘录模式C++
备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不暴露对象内部状态的情况下,捕获并保存对象的内部状态,以便在将来需要时恢复该状态。这种模式实现了对象状态的快照保存与恢复,常用于撤销操作、历史记录等场景。
备忘录模式的核心角色
- Originator(发起人):创建一个备忘录,用于记录自身当前状态;也可使用备忘录恢复状态
- Memento(备忘录):存储发起人的内部状态,对发起人以外的对象隐藏状态细节
- Caretaker(负责人):负责保存备忘录,但不能对备忘录的内容进行操作或检查
C++实现示例
以下以"文本编辑器的撤销功能"为例实现备忘录模式,支持保存文本的历史状态并在需要时恢复:
#include <iostream>
#include <string>
#include <vector>
#include <memory>// 备忘录类:存储文本状态(对外部隐藏实现细节)
class TextMemento {
private:// 只有发起人可以访问备忘录的状态friend class TextEditor;std::string content; // 文本内容int cursorPos; // 光标位置// 私有构造函数,防止外部创建TextMemento(std::string cont, int pos) : content(std::move(cont)), cursorPos(pos) {}
};// 发起人:文本编辑器
class TextEditor {
private:std::string content; // 当前文本内容int cursorPos; // 当前光标位置public:TextEditor() : cursorPos(0) {}// 输入文本void type(const std::string& text) {content.insert(cursorPos, text);cursorPos += text.length();}// 移动光标void moveCursor(int pos) {if (pos >= 0 && pos <= static_cast<int>(content.length())) {cursorPos = pos;}}// 创建备忘录(保存当前状态)std::unique_ptr<TextMemento> save() const {return std::make_unique<TextMemento>(content, cursorPos);}// 从备忘录恢复状态void restore(const TextMemento* memento) {if (memento) {content = memento->content;cursorPos = memento->cursorPos;}}// 显示当前状态void display() const {std::cout << "文本内容: " << content << std::endl;std::cout << "光标位置: " << cursorPos << std::endl;// 显示光标位置指示std::cout << " " << std::string(cursorPos, ' ') << "^" << std::endl;}// 获取当前内容长度(用于测试)size_t getContentLength() const {return content.length();}
};// 负责人:历史记录管理器
class HistoryManager {
private:std::vector<std::unique_ptr<TextMemento>> mementos; // 存储备忘录int currentIndex; // 当前状态索引public:HistoryManager() : currentIndex(-1) {}// 保存新状态void saveState(std::unique_ptr<TextMemento> memento) {// 清除当前状态之后的历史(如果有)if (currentIndex < static_cast<int>(mementos.size()) - 1) {mementos.erase(mementos.begin() + currentIndex + 1, mementos.end());}// 添加新状态mementos.push_back(std::move(memento));currentIndex = mementos.size() - 1;}// 撤销操作(恢复到上一个状态)const TextMemento* undo() {if (currentIndex > 0) {currentIndex--;return mementos[currentIndex].get();}std::cout << "已到达最早状态,无法继续撤销" << std::endl;return nullptr;}// 重做操作(恢复到下一个状态)const TextMemento* redo() {if (currentIndex < static_cast<int>(mementos.size()) - 1) {currentIndex++;return mementos[currentIndex].get();}std::cout << "已到达最新状态,无法继续重做" << std::endl;return nullptr;}// 获取历史记录数量size_t getHistoryCount() const {return mementos.size();}
};// 客户端代码
int main() {// 创建文本编辑器和历史管理器TextEditor editor;HistoryManager history;// 第一次操作std::cout << "=== 第一次输入 ===" << std::endl;editor.type("Hello, ");editor.display();history.saveState(editor.save()); // 保存状态1// 第二次操作std::cout << "\n=== 第二次输入 ===" << std::endl;editor.type("World!");editor.display();history.saveState(editor.save()); // 保存状态2// 第三次操作std::cout << "\n=== 第三次输入 ===" << std::endl;editor.moveCursor(7); // 移动光标到逗号后editor.type("Beautiful ");editor.display();history.saveState(editor.save()); // 保存状态3// 撤销一次std::cout << "\n=== 第一次撤销 ===" << std::endl;editor.restore(history.undo());editor.display();// 再撤销一次std::cout << "\n=== 第二次撤销 ===" << std::endl;editor.restore(history.undo());editor.display();// 重做一次std::cout << "\n=== 第一次重做 ===" << std::endl;editor.restore(history.redo());editor.display();return 0;
}
代码解析
-
备忘录(
TextMemento
):- 私有构造函数确保只有发起人(
TextEditor
)可以创建它 - 通过
friend
关键字让发起人访问其内部状态(content
和cursorPos
) - 对负责人和客户端隐藏状态细节,保证封装性
- 私有构造函数确保只有发起人(
-
发起人(
TextEditor
):- 维护文本编辑器的当前状态(内容和光标位置)
- 提供
save()
方法创建备忘录,捕获当前状态 - 提供
restore()
方法从备忘录恢复状态 - 包含文本编辑的核心功能(输入、移动光标等)
-
负责人(
HistoryManager
):- 管理备忘录的存储和检索,不直接操作备忘录内容
- 支持撤销(
undo()
)和重做(redo()
)功能 - 维护当前状态索引,正确管理历史记录的顺序
-
工作流程:
- 编辑器每完成一次操作,通过
save()
创建备忘录并交由负责人保存 - 需要撤销时,负责人返回上一个备忘录,编辑器用其恢复状态
- 重做功能则恢复到撤销前的状态,实现了灵活的状态回溯
- 编辑器每完成一次操作,通过
备忘录模式的优缺点
优点:
- 实现了状态的封装与恢复,发起人无需暴露内部细节
- 客户端可以通过负责人灵活地管理历史状态(撤销/重做)
- 负责人与发起人解耦,两者各司其职(状态管理与业务逻辑)
- 便于保存和恢复对象的任意历史状态
缺点:
- 如果对象状态较大或频繁保存,会消耗较多内存
- 备忘录可能包含大量状态数据,复制成本较高
- 负责人需要管理大量备忘录,增加了系统复杂度
适用场景
- 当需要保存和恢复对象的历史状态时(如撤销操作)
- 当不希望暴露对象的内部状态,却需要捕获和恢复状态时
- 当需要维护对象的历史记录,以便后续分析或回滚时
常见应用:
- 文本编辑器的撤销/重做功能
- 数据库事务的提交与回滚
- 游戏中的存档与读档功能
- 图形编辑软件的历史记录功能
备忘录模式的关键是平衡封装性和灵活性:发起人保持对状态的控制,备忘录隐藏实现细节,负责人提供便捷的状态管理接口,三者协作实现安全高效的状态快照机制。