👨🎓 模式名称:备忘录模式(Memento)
👦 故事背景:
小明最近在开发一个"收益记录系统"📊,
每天的收入、支出都自动记录到账本中。
他每天都能看到当日盈亏,非常开心:
"今天赚了300块奶茶钱!明天请甜妹喝咖啡☕!"
但有天他手滑写了个bug 💥,把所有账本清零了!
他想恢复昨天的记录,却发现......
他没有一个安全的备份机制,而且账本的内部数据是私有成员变量,
外部系统无法访问。
❌ 没有使用备忘录模式的做法(破坏封装)
cpp
#include <iostream>
#include <vector>
#include <string>
class Ledger {
public:
std::vector<std::string> records; // 🚫 暴露内部数据(封装性被破坏)
void addRecord(const std::string& r) {
records.push_back(r);
}
void show() const {
std::cout << "📘 当前账本内容:" << std::endl;
for (auto& r : records)
std::cout << " - " << r << std::endl;
}
};
int main() {
Ledger ledger;
ledger.addRecord("收入 +300 元");
ledger.addRecord("支出 -50 元");
// 外部直接拷贝整个账本(破坏封装)
auto backup = ledger.records;
ledger.addRecord("误操作:清空账本!");
ledger.records.clear();
std::cout << "💥 崩溃后账本:" << std::endl;
ledger.show();
// 恢复
ledger.records = backup;
std::cout << "\n✅ 恢复后的账本:" << std::endl;
ledger.show();
}
⚠️ 问题分析:
1、破坏封装性:外部直接访问 records,违反了对象设计原则。
2、风险高:外部代码能随意修改账本内容。
3、紧耦合:备份逻辑必须知道内部数据结构。
✅ 使用备忘录模式的正确做法(保持封装性)
cpp
#include <iostream>
#include <vector>
#include <string>
#include <memory>
// 备忘录类,只能被 Ledger 操作
class LedgerMemento {
std::vector<std::string> state;
friend class Ledger; // 仅 Ledger 可访问内部状态
LedgerMemento(const std::vector<std::string>& s) : state(s) {}
public:
// 为调试方便,可以提供非破坏性访问(可选)
// const std::vector<std::string>& getState() const { return state; }
};
// 发起者:账本
class Ledger {
private:
std::vector<std::string> records; // 私有成员
public:
void addRecord(const std::string& r) {
records.push_back(r);
}
void clear() {
records.clear();
}
void show() const {
std::cout << "当前账本内容:" << std::endl;
if (records.empty()) std::cout << " (空)" << std::endl;
for (auto& r : records)
std::cout << " - " << r << std::endl;
}
// 创建备忘录(封装状态)
std::shared_ptr<LedgerMemento> createMemento() const {
return std::shared_ptr<LedgerMemento>(new LedgerMemento(records));
}
// 从备忘录恢复状态
void restore(const std::shared_ptr<LedgerMemento>& memento) {
if (memento) records = memento->state;
}
};
// 负责人:管理备份历史
class LedgerCaretaker {
private:
std::shared_ptr<LedgerMemento> backup;
public:
void save(const std::shared_ptr<LedgerMemento>& m) {
backup = m;
}
std::shared_ptr<LedgerMemento> getBackup() {
return backup;
}
};
// 测试
int main() {
Ledger ledger;
LedgerCaretaker caretaker;
ledger.addRecord("收入 +300 元");
ledger.addRecord("支出 -50 元");
// 保存备份
caretaker.save(ledger.createMemento());
ledger.addRecord("误操作:清空账本!");
ledger.clear();
std::cout << "崩溃后账本:" << std::endl;
ledger.show();
// 恢复备份
ledger.restore(caretaker.getBackup());
std::cout << "\n恢复后的账本:" << std::endl;
ledger.show();
return 0;
}
对比分析
| 对比项 | 没用备忘录模式 | 使用备忘录模式 |
|---|---|---|
| 封装性 | ❌ 暴露内部结构 | ✅ 内部状态完全封装 |
| 安全性 | ⚠️ 外部可随意篡改 | 🔒 只有 Ledger 能访问状态 |
| 可维护性 | 修改内部结构需改外部备份逻辑 | 内部可自由变更,不影响外部 |