一、备忘录模式简介
备忘录模式(Memento Pattern)是一种行为型设计模式,又叫做快照模式,它允许在不破坏对象封装性的前提下捕获并保存一个对象的内部状态,以便在将来能够将对象恢复到原先保存的状态。
GoF一书中对备忘录模式的介绍
Originator(发起人)
- 负责创建一个备忘录Memento,记录当前时刻的内部状态。
- 提供创建备忘录和恢复备忘录数据的功能。
- 可以访问备忘录里的所有信息。
Memento(备忘录)
- 负责存储Originator的内部状态。
- 防止Originator以外的对象访问备忘录。
- 提供接口供Originator获取和设置状态。
Caretaker(管理者)
- 负责管理备忘录Memento。
- 不能访问或操作Memento的内容。
二、备忘录模式的设计方法
使用备忘录模式来管理游戏角色的状态。通过CareTaker类,用户可以保存和恢复角色的状态,从而实现游戏状态的撤销和回滚功能。这个设计模式在需要保存和恢复对象状态的应用中非常有用,例如撤销操作、恢复功能等。
memento.cpp
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <stdexcept>
// 备忘录
class Memento {
public:
Memento(int health, int level) : health(health), level(level) {}
int getHealth() const { return health; }
int getLevel() const { return level; }
private:
int health;
int level;
};
// 游戏角色(Originator)
class GameCharacter {
public:
GameCharacter(int health, int level) : health(health), level(level) {}
void setHealth(int newHealth) { health = newHealth; }
void setLevel(int newLevel) { level = newLevel; }
Memento save() const {
return Memento(health, level);
}
void restore(const Memento& memento) {
health = memento.getHealth();
level = memento.getLevel();
}
bool SimulateStatus() const {
std::cout << "健康值: " << health << ", 等级: " << level << std::endl;
return health > 0;
}
private:
int health;
int level;
};
// 管理者
class CareTaker {
public:
void saveMemento(const Memento& memento, size_t index) {
undoStack.push({memento, index});
rollbackStack.push({memento, index});
}
void undo(GameCharacter& character) {
if (undoStack.empty()) {
std::cerr << "没有上一份状态,不需要撤销." << std::endl;
return;
}
MementoData mementoData = undoStack.top();
undoStack.pop();
rollbackStack.push(mementoData);
if (!undoStack.empty()) {
MementoData toData = undoStack.top();
character.restore(toData.memento);
}
}
void rollback(GameCharacter& character, size_t index) {
if (index >= rollbackStack.size()) {
std::cerr << "无效的回滚状态." << std::endl;
return;
}
std::stack<MementoData> tempStack;
while (rollbackStack.size() > index) {
tempStack.push(rollbackStack.top());
rollbackStack.pop();
}
if (!rollbackStack.empty()) {
MementoData mementoData = rollbackStack.top();
character.restore(mementoData.memento);
}
while (!tempStack.empty()) {
rollbackStack.push(tempStack.top());
tempStack.pop();
}
}
void GameStatus() const {
std::stack<MementoData> tempStack = undoStack;
std::cout << "可回滚状态索引及对应的健康值和等级:\n";
while (!tempStack.empty()) {
MementoData mementoData = tempStack.top();
tempStack.pop();
std::cout << "索引:" << mementoData.index \
<< " 健康值:" << mementoData.memento.getHealth() \
<< " 等级:" << mementoData.memento.getLevel() << std::endl;
}
}
private:
struct MementoData {
Memento memento;
size_t index;
};
std::stack<MementoData> undoStack;
std::stack<MementoData> rollbackStack;
};
//demo
void doWorking() {
// 创建游戏角色
GameCharacter WuKong(100,0);
std::cout << "初始状态: ";
WuKong.SimulateStatus();
// 创建管理者
CareTaker BaJie;
BaJie.saveMemento(WuKong.save(), 0);
// 模拟游戏进展,角色状态发生变化
WuKong.setHealth(20);
WuKong.setLevel(1);
std::cout << "'一难'战斗结束后状态, ";
WuKong.SimulateStatus();
BaJie.saveMemento(WuKong.save(), 1);
WuKong.setHealth(50);
WuKong.setLevel(6);
std::cout << "'六难'战斗结束后状态, ";
WuKong.SimulateStatus();
BaJie.saveMemento(WuKong.save(), 2);
WuKong.setHealth(90);
WuKong.setLevel(9);
std::cout << "'九难'战斗结束后状态,";
WuKong.SimulateStatus();
BaJie.saveMemento(WuKong.save(), 3);
WuKong.setHealth(100);
WuKong.setLevel(50);
std::cout << "'五十难'战斗结束后状态, ";
WuKong.SimulateStatus();
BaJie.saveMemento(WuKong.save(), 4);
WuKong.setHealth(1);
WuKong.setLevel(80);
std::cout << "'八十难'战斗结束后状态, ";
WuKong.SimulateStatus();
BaJie.saveMemento(WuKong.save(), 5);
WuKong.setHealth(0);
WuKong.setLevel(81);
std::cout << "'八十一难'战斗失败状态, ";
WuKong.SimulateStatus();
BaJie.saveMemento(WuKong.save(), 6);
//模拟游戏闯关失败
if (! WuKong.SimulateStatus()) {
std::string select;
std::cout << "\n闯关失败!\n请选择回到上一个关卡(undo)?重新开始玩游戏(rollback): ";
std::cin >> select;
if (select == "undo") {
// 模拟战斗失败后撤销到上一次状态
BaJie.undo(WuKong);
std::cout << "恢复至上一次状态: ";
WuKong.SimulateStatus();
}
else if (select == "rollback") {
//模拟回滚到指定状态
BaJie.GameStatus();
size_t index;
std::cout << "\n请输入回滚到的状态(从0开始): ";
std::cin >> index;
std::cout << "恢复至第" << index << "次的状态: ";
BaJie.rollback(WuKong, index+1);
WuKong.SimulateStatus();
}
else {
std::cout << "游戏结束,欢迎再次体验。" << std::endl;
}
}
return;
}
int main() {
doWorking();
return 0;
}
运行效果
三、备忘录模式应用场景
1. 文本编辑器
在文本编辑器中,用户可能会进行多次编辑操作。备忘录模式可以用来保存每次编辑前的文档状态,以便在用户选择“撤销”操作时恢复到之前的状态。这可以包括:
- 文字的插入、删除或格式修改。
- 撤销和重做功能。
2. 游戏开发
在游戏中,角色的状态(如生命值、等级、装备等)需要频繁保存和恢复。例如:
- 玩家在不同关卡的进度。
- 角色在战斗中的状态变化,允许玩家在战斗失败后返回上一个状态。
3. 事务管理
在事务处理系统中,可以使用备忘录模式来保存事务的状态,以便在事务失败时能够回滚到之前的状态。这在数据库管理和金融系统中特别重要。
4. 图形编辑器
在图形编辑软件中,用户的每个操作(如移动、缩放、旋转对象)都可能需要被保存,以便用户可以轻松地撤销或重做这些操作。备忘录模式可以管理这些状态,支持:
- 对图形对象的多级撤销和重做。
- 保存不同图形状态的快照。
5. 程序设置
在应用程序中,用户可能会更改多个设置(如主题、布局等)。备忘录模式可以在每次更改时保存设置的快照,以便用户在需要时恢复到之前的设置。
6. IDE(集成开发环境)
在代码编辑器或IDE中,备忘录模式可以用于管理代码的不同版本。用户可以在编写代码时保存每个重要的版本,以便在出现问题时恢复到某个特定的代码状态。
7. Web 应用中的表单
在Web应用中,用户填写表单时,可能希望在离开页面后返回并继续填写。备忘录模式可以用来保存表单的当前状态,以便用户可以在返回时恢复之前的输入。
8. 聊天应用
在聊天应用中,用户可能希望撤销发送的消息或恢复删除的消息。备忘录模式可以保存每条消息的状态,以支持撤销功能。
四、总结
备忘录设计模式能够在不暴露对象内部结构的情况下保存和恢复其状态 (不破坏类的封装性)。