注意:复现代码时,确保 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 | | 触发保存与恢复 |
+------------------+ +----------------+
角色说明
Originator
:原发器,生成状态快照(createMemento
)并从快照恢复(restore
)。Memento
:备忘录,存储原发器的内部状态(仅允许原发器访问)。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. 懒加载备忘录:
- 仅在需要恢复时生成快照,结合持久化存储(如磁盘缓存)。