目录
- 一、模式核心概念与结构
- [二、C++ 实现示例:文本编辑器撤销功能](#二、C++ 实现示例:文本编辑器撤销功能)
- 三、备忘录模式的关键特性
- 四、应用场景
- 五、备忘录模式与其他设计模式的关系
- [六、C++ 标准库中的备忘录模式应用](#六、C++ 标准库中的备忘录模式应用)
- 七、优缺点分析
- 八、实战案例:游戏角色状态存档
- 九、实现注意事项
备忘录模式(Memento Pattern)是一种【行为型】设计模式,它允许在不破坏对象封装性的前提下,捕获并保存对象的内部状态,以便后续可以将对象恢复到先前的状态。这种模式通过将状态保存和恢复操作分离,实现了对象状态管理的职责分离。
一、模式核心概念与结构
备忘录模式包含三个核心角色:
- 原发器(Originator):创建备忘录并使用备忘录恢复内部状态的对象。
- 备忘录(Memento):存储原发器内部状态的对象,通常提供有限的访问权限。
- 管理者(Caretaker):负责保存备忘录,但不检查或修改备忘录的内容。
二、C++ 实现示例:文本编辑器撤销功能
以下是一个简单的文本编辑器示例,演示如何使用备忘录模式实现撤销功能:
cpp
#include <iostream>
#include <string>
#include <memory>
#include <vector>
// 前向声明
class Memento;
// 原发器:文本编辑器
class TextEditor {
private:
std::string content;
public:
void setContent(const std::string& text) {
content = text;
std::cout << "Current content: " << content << std::endl;
}
std::string getContent() const {
return content;
}
// 创建备忘录
std::shared_ptr<Memento> createMemento() const;
// 从备忘录恢复
void restoreFromMemento(std::shared_ptr<Memento> memento);
};
// 备忘录
class Memento {
private:
std::string savedContent;
public:
Memento(const std::string& content) : savedContent(content) {}
// 仅供原发器访问的内部状态
friend class TextEditor;
};
// 实现原发器的方法
std::shared_ptr<Memento> TextEditor::createMemento() const {
return std::make_shared<Memento>(content);
}
void TextEditor::restoreFromMemento(std::shared_ptr<Memento> memento) {
content = memento->savedContent;
std::cout << "Restored content: " << content << std::endl;
}
// 管理者:历史记录
class History {
private:
std::vector<std::shared_ptr<Memento>> mementos;
public:
void saveState(std::shared_ptr<Memento> memento) {
mementos.push_back(memento);
std::cout << "State saved. History size: " << mementos.size() << std::endl;
}
std::shared_ptr<Memento> getPreviousState() {
if (mementos.empty()) {
std::cout << "No previous state" << std::endl;
return nullptr;
}
auto last = mementos.back();
mementos.pop_back();
std::cout << "Restoring to previous state. History size: " << mementos.size() << std::endl;
return last;
}
};
// 客户端代码
int main() {
// 创建文本编辑器和历史记录管理器
auto editor = std::make_shared<TextEditor>();
auto history = std::make_shared<History>();
// 编辑文本并保存状态
editor->setContent("Hello");
history->saveState(editor->createMemento());
editor->setContent("Hello World");
history->saveState(editor->createMemento());
editor->setContent("Hello World!");
history->saveState(editor->createMemento());
// 撤销操作
editor->restoreFromMemento(history->getPreviousState()); // 恢复到 "Hello World"
editor->restoreFromMemento(history->getPreviousState()); // 恢复到 "Hello"
return 0;
}
三、备忘录模式的关键特性
- 封装状态 :
- 备忘录对象封装了原发器的内部状态,保护其不被外部访问。
- 状态管理分离 :
- 管理者负责保存备忘录,原发器负责创建和恢复状态,实现职责分离。
- 简化原发器 :
- 原发器无需管理自己的历史记录,降低了复杂度。
- 支持撤销和恢复 :
- 通过保存多个备忘录,可以实现多级撤销和恢复操作。
四、应用场景
- 撤销 / 重做功能 :
- 文本编辑器、图形设计工具、游戏中的撤销操作。
- 例如,Photoshop 中的历史记录面板。
- 事务管理 :
- 数据库事务的回滚机制。
- 例如,数据库在执行操作前保存状态,失败时回滚。
- 游戏存档 :
- 保存游戏进度,支持读档功能。
- 对象版本控制 :
- 保存对象的不同版本,支持版本回退。
五、备忘录模式与其他设计模式的关系
- 命令模式 :
- 命令模式可以使用备忘录模式来实现命令的撤销功能。
- 命令对象执行操作前创建备忘录,撤销时恢复状态。
- 迭代器模式 :
- 可以结合迭代器模式遍历备忘录列表,实现历史记录的浏览。
- 原型模式 :
- 备忘录可以通过克隆(原型模式)来创建对象的副本。
六、C++ 标准库中的备忘录模式应用
- 智能指针 :
std::shared_ptr
和std::unique_ptr
可以保存对象状态,支持资源管理。
- 序列化 :
- 通过
std::stringstream
或 Boost.Serialization 可以将对象状态保存到流中。
- 通过
- 异常处理 :
- C++ 的异常处理机制可以看作是一种隐式的备忘录模式,异常抛出时保存调用栈状态。
七、优缺点分析
优点:
- 封装性:保护对象内部状态不被外部访问。
- 简化设计:将状态管理逻辑从原发器中分离。
- 支持撤销:方便实现多级撤销和重做功能。
- 符合开闭原则:新增备忘录类型无需修改现有代码。
缺点:
- 资源消耗:保存大量备忘录可能占用过多内存。
- 性能影响:频繁创建和恢复备忘录可能影响性能。
- 维护成本:如果原发器状态复杂,备忘录的维护可能变得困难。
八、实战案例:游戏角色状态存档
以下是一个游戏角色状态存档的实现示例,支持角色状态的保存和恢复:
cpp
#include <iostream>
#include <string>
#include <memory>
#include <vector>
// 前向声明
class CharacterMemento;
// 原发器:游戏角色
class GameCharacter {
private:
std::string name;
int health;
int level;
std::string weapon;
public:
GameCharacter(const std::string& n, int h, int l, const std::string& w)
: name(n), health(h), level(l), weapon(w) {}
void takeDamage(int damage) {
health -= damage;
if (health < 0) health = 0;
std::cout << name << " takes " << damage << " damage. Health: " << health << std::endl;
}
void levelUp() {
level++;
std::cout << name << " levels up to " << level << std::endl;
}
void changeWeapon(const std::string& newWeapon) {
weapon = newWeapon;
std::cout << name << " changes weapon to " << weapon << std::endl;
}
void displayStatus() const {
std::cout << "=== " << name << " Status ===" << std::endl;
std::cout << "Health: " << health << std::endl;
std::cout << "Level: " << level << std::endl;
std::cout << "Weapon: " << weapon << std::endl;
std::cout << "====================" << std::endl;
}
// 创建备忘录
std::shared_ptr<CharacterMemento> saveState() const;
// 从备忘录恢复
void restoreState(std::shared_ptr<CharacterMemento> memento);
};
// 备忘录
class CharacterMemento {
private:
std::string savedName;
int savedHealth;
int savedLevel;
std::string savedWeapon;
public:
CharacterMemento(const std::string& n, int h, int l, const std::string& w)
: savedName(n), savedHealth(h), savedLevel(l), savedWeapon(w) {}
// 仅供原发器访问的内部状态
friend class GameCharacter;
};
// 实现原发器的方法
std::shared_ptr<CharacterMemento> GameCharacter::saveState() const {
return std::make_shared<CharacterMemento>(name, health, level, weapon);
}
void GameCharacter::restoreState(std::shared_ptr<CharacterMemento> memento) {
name = memento->savedName;
health = memento->savedHealth;
level = memento->savedLevel;
weapon = memento->savedWeapon;
std::cout << name << " restored to previous state" << std::endl;
}
// 管理者:游戏存档系统
class GameSaveSystem {
private:
std::vector<std::shared_ptr<CharacterMemento>> saves;
public:
void saveGame(std::shared_ptr<CharacterMemento> memento) {
saves.push_back(memento);
std::cout << "Game saved. Save count: " << saves.size() << std::endl;
}
std::shared_ptr<CharacterMemento> loadGame(int index) {
if (index < 0 || index >= saves.size()) {
std::cout << "Invalid save index" << std::endl;
return nullptr;
}
std::cout << "Loading save " << index << std::endl;
return saves[index];
}
void displaySaves() const {
std::cout << "=== Save Files ===" << std::endl;
for (size_t i = 0; i < saves.size(); i++) {
std::cout << i << ". Save File" << std::endl;
}
std::cout << "==================" << std::endl;
}
};
// 客户端代码
int main() {
// 创建游戏角色和存档系统
auto player = std::make_shared<GameCharacter>("Hero", 100, 1, "Sword");
auto saveSystem = std::make_shared<GameSaveSystem>();
// 显示初始状态
player->displayStatus();
// 保存初始状态
saveSystem->saveGame(player->saveState());
// 进行一些操作
player->takeDamage(20);
player->levelUp();
player->changeWeapon("Greatsword");
// 显示当前状态
player->displayStatus();
// 保存当前状态
saveSystem->saveGame(player->saveState());
// 再进行一些操作
player->takeDamage(50);
player->displayStatus();
// 恢复到第一个存档
player->restoreState(saveSystem->loadGame(0));
player->displayStatus();
// 恢复到第二个存档
player->restoreState(saveSystem->loadGame(1));
player->displayStatus();
return 0;
}
九、实现注意事项
- 访问控制 :
- 备忘录的状态应仅由原发器访问,可通过友元类或接口隔离实现。
- 备忘录大小 :
- 避免保存不必要的状态,减少内存消耗。对于大型对象,可考虑增量保存。
- 生命周期管理 :
- 确保管理者正确管理备忘录的生命周期,避免内存泄漏。
- 序列化支持 :
- 对于需要持久化的场景,备忘录应支持序列化和反序列化。
备忘录模式是 C++ 中实现对象状态管理的重要工具,通过封装状态保存和恢复逻辑,使系统在不破坏封装性的前提下支持撤销、重做和历史记录等功能,提高了代码的可维护性和用户体验。
如果这篇文章对你有所帮助,渴望获得你的一个点赞!
