备忘录模式(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()创建备忘录并交由负责人保存 - 需要撤销时,负责人返回上一个备忘录,编辑器用其恢复状态
- 重做功能则恢复到撤销前的状态,实现了灵活的状态回溯
- 编辑器每完成一次操作,通过
备忘录模式的优缺点
优点:
- 实现了状态的封装与恢复,发起人无需暴露内部细节
- 客户端可以通过负责人灵活地管理历史状态(撤销/重做)
- 负责人与发起人解耦,两者各司其职(状态管理与业务逻辑)
- 便于保存和恢复对象的任意历史状态
缺点:
- 如果对象状态较大或频繁保存,会消耗较多内存
- 备忘录可能包含大量状态数据,复制成本较高
- 负责人需要管理大量备忘录,增加了系统复杂度
适用场景
- 当需要保存和恢复对象的历史状态时(如撤销操作)
- 当不希望暴露对象的内部状态,却需要捕获和恢复状态时
- 当需要维护对象的历史记录,以便后续分析或回滚时
常见应用:
- 文本编辑器的撤销/重做功能
- 数据库事务的提交与回滚
- 游戏中的存档与读档功能
- 图形编辑软件的历史记录功能
备忘录模式的关键是平衡封装性和灵活性:发起人保持对状态的控制,备忘录隐藏实现细节,负责人提供便捷的状态管理接口,三者协作实现安全高效的状态快照机制。
1597

被折叠的 条评论
为什么被折叠?



