C++备忘录模式
·
备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获和保存对象的内部状态,并在需要时恢复该状态。这种模式非常适合实现撤销(Undo)功能,或者需要保存对象历史状态的场景。
备忘录模式的核心角色
- 原发器(Originator):需要保存状态的对象,能够创建备忘录和从备忘录恢复状态
- 备忘录(Memento):存储原发器的内部状态,防止原发器以外的对象访问其内容
- 负责人(Caretaker):负责保存备忘录,但不能对备忘录的内容进行操作或检查
备忘录模式的实现示例
下面以"文本编辑器"为例展示备忘录模式的实现,编辑器可以保存当前文本状态,支持撤销操作回到之前的状态:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 前向声明
class TextEditor;
// 备忘录:存储文本编辑器的状态
class Memento {
private:
// 只有TextEditor可以访问备忘录的内容(友元)
friend class TextEditor;
std::string text; // 文本内容
int cursorPosition; // 光标位置
// 私有构造函数,防止外部创建
Memento(const std::string& txt, int pos)
: text(txt), cursorPosition(pos) {}
};
// 原发器:文本编辑器
class TextEditor {
private:
std::string text;
int cursorPosition;
public:
TextEditor() : cursorPosition(0) {}
// 设置文本
void setText(const std::string& newText) {
text = newText;
cursorPosition = text.length();
}
// 添加文本
void addText(const std::string& addedText) {
text.insert(cursorPosition, addedText);
cursorPosition += addedText.length();
}
// 移动光标
void moveCursor(int position) {
if (position >= 0 && position <= static_cast<int>(text.length())) {
cursorPosition = position;
}
}
// 创建备忘录,保存当前状态
std::unique_ptr<Memento> createMemento() const {
return std::make_unique<Memento>(text, cursorPosition);
}
// 从备忘录恢复状态
void restoreFromMemento(const Memento* memento) {
if (memento) {
text = memento->text;
cursorPosition = memento->cursorPosition;
}
}
// 显示当前状态
void display() const {
std::cout << "当前文本: " << text << std::endl;
std::cout << "光标位置: " << cursorPosition << std::endl;
}
};
// 负责人:管理备忘录(历史记录)
class Caretaker {
private:
std::vector<std::unique_ptr<Memento>> history;
int currentIndex; // 当前状态索引
public:
Caretaker() : currentIndex(-1) {}
// 保存状态
void saveState(TextEditor* editor) {
// 清除当前状态之后的历史(如多次撤销后再操作)
if (currentIndex < static_cast<int>(history.size()) - 1) {
history.erase(history.begin() + currentIndex + 1, history.end());
}
// 保存新状态
history.push_back(editor->createMemento());
currentIndex = history.size() - 1;
}
// 撤销操作
bool undo(TextEditor* editor) {
if (currentIndex > 0) {
currentIndex--;
editor->restoreFromMemento(history[currentIndex].get());
return true;
}
return false; // 已经是最早状态
}
// 重做操作
bool redo(TextEditor* editor) {
if (currentIndex < static_cast<int>(history.size()) - 1) {
currentIndex++;
editor->restoreFromMemento(history[currentIndex].get());
return true;
}
return false; // 已经是最新状态
}
};
// 客户端使用
int main() {
TextEditor editor;
Caretaker caretaker;
// 初始状态
editor.setText("Hello, ");
caretaker.saveState(&editor);
std::cout << "=== 初始状态 ===" << std::endl;
editor.display();
// 第一次修改
editor.addText("World!");
caretaker.saveState(&editor);
std::cout << "\n=== 第一次修改后 ===" << std::endl;
editor.display();
// 第二次修改
editor.moveCursor(7);
editor.addText("beautiful ");
caretaker.saveState(&editor);
std::cout << "\n=== 第二次修改后 ===" << std::endl;
editor.display();
// 撤销一次
if (caretaker.undo(&editor)) {
std::cout << "\n=== 撤销一次后 ===" << std::endl;
editor.display();
}
// 再撤销一次
if (caretaker.undo(&editor)) {
std::cout << "\n=== 再撤销一次后 ===" << std::endl;
editor.display();
}
// 重做一次
if (caretaker.redo(&editor)) {
std::cout << "\n=== 重做一次后 ===" << std::endl;
editor.display();
}
return 0;
}
备忘录模式的工作原理
- 原发器创建一个备忘录,用于保存自身当前的内部状态
- 负责人存储这个备忘录,但不能查看或修改备忘录的内容
- 当需要恢复状态时,原发器从负责人那里获取备忘录,并使用其中的数据恢复自身状态
- 备忘录的内容只能由原发器访问,确保了封装性
备忘录模式的两种实现方式
- 窄接口:备忘录只提供有限的接口(如示例中通过友元实现),只有原发器能访问其内部状态
- 宽接口:备忘录提供较宽的接口,允许其他对象访问其内部状态,这种方式可能破坏封装性
备忘录模式与命令模式的区别
- 备忘录模式:专注于保存和恢复对象的状态,主要用于实现撤销功能
- 命令模式:专注于封装请求,除了撤销外,还支持命令排队、日志记录等
备忘录模式的应用场景
- 需要实现撤销/重做功能的场景(如文本编辑器、图形编辑器)
- 需要保存对象历史状态,以便后续分析或恢复的场景
- 需要在不暴露对象内部结构的前提下,捕获和恢复对象状态的场景
备忘录模式的优缺点
优点:
- 实现了状态的封装,保存和恢复状态不会破坏对象的封装性
- 提供了状态恢复机制,支持撤销操作
- 负责人与原发器解耦,负责人无需了解状态的细节
缺点:
- 如果对象状态较大,频繁创建备忘录会消耗较多内存
- 每保存一次状态都需要创建一个新的备忘录对象,可能影响性能
- 如果原发器的状态变化频繁且复杂,备忘录的管理会变得困难
备忘录模式在许多应用程序中都有广泛应用,例如:
- 文本编辑器的撤销/重做功能
- 图形设计软件中的历史记录功能
- 数据库事务的回滚机制
- 游戏中的存档和读档功能
这种模式通过巧妙的封装,既实现了状态的保存与恢复,又不破坏对象的封装性,是实现撤销功能的首选模式。
更多推荐

所有评论(0)