设计模式(C++)详解——备忘录模式(2)
<摘要>
备忘录模式是一种行为设计模式,它允许在不破坏对象封装性的前提下捕获和存储对象的内部状态,并在需要时将对象恢复到之前的状态。本文从模式的历史起源、核心概念入手,深入分析其设计哲学和实现考量,通过文本编辑器、游戏存档、绘图程序等多个实际案例展示具体应用,结合详细的C++代码实现和Mermaid图表说明,全面解析备忘录模式在软件开发中的价值和实现技巧。
<解析>
备忘录模式深度解析:时光倒流的艺术
1. 历史渊源与演进脉络
1.1 模式的诞生背景
备忘录模式的起源可以追溯到软件工程中一个永恒的需求:"撤销"功能。在早期的交互式软件中,用户期望能够回退操作,就像在现实生活中我们有时需要"反悔"一样。
时间线演进:
时期 | 发展里程碑 | 代表性系统 |
---|---|---|
1970s | 早期文本编辑器的简单撤销 | EMACS, Vi |
1980s | 图形界面软件的撤销/重做 | MacPaint, Windows Paint |
1994年 | GoF正式定义备忘录模式 | 《设计模式》出版 |
2000s | 模式在游戏和Web应用中普及 | Photoshop, Word, 网页浏览器 |
现今 | 分布式系统的事务回滚 | 微服务架构, 数据库系统 |
1.2 现状与发展趋势
当前应用现状:
- 🎯 普遍性:几乎所有交互式软件都实现了某种形式的撤销功能
- 🎯 复杂性:从简单的单步撤销发展到多级撤销树
- 🎯 性能优化:增量保存、压缩存储等技术的应用
未来发展趋势:
- 🚀 AI增强:智能撤销建议、模式识别
- 🚀 跨设备同步:云备份的撤销历史
- 🚀 实时协作:多人协同编辑的冲突解决
2. 核心概念深度剖析
2.1 三大核心角色详解
Originator(发起人)
职责:需要保存状态的对象
特征:
- 拥有重要的内部状态
- 能够创建备忘录来保存当前状态
- 能够使用备忘录恢复之前的状态
/*** @brief 发起人抽象定义* * 定义了创建和恢复备忘录的标准接口,是模式的核心参与者*/
class Originator {
protected:// 需要保存的内部状态StateType internalState_;public:/*** @brief 创建状态备忘录* * 捕获当前对象的内部状态,并封装在备忘录对象中* * @return Memento* 包含当前状态的备忘录对象*/virtual std::unique_ptr<Memento> createMemento() const = 0;/*** @brief 从备忘录恢复状态* * 使用备忘录中保存的状态来恢复对象的内部状态* * @param memento 包含要恢复状态的备忘录*/virtual void restoreFromMemento(const Memento* memento) = 0;/*** @brief 业务操作* * 改变对象状态的操作,通常在执行后会创建备忘录*/virtual void performOperation() = 0;
};
Memento(备忘录)
职责:状态存储的封装容器
特征:
- 存储发起人的内部状态
- 防止发起人之外的对象访问其内容
- 提供有限的接口给发起人
/*** @brief 备忘录基类* * 定义备忘录的基本接口,具体的状态存储由子类实现*/
class Memento {
public:virtual ~Memento() = default;/*** @brief 获取时间戳* * 用于记录状态保存的时间,支持按时间排序* * @return std::chrono::system_clock::time_point 保存时间*/virtual std::chrono::system_clock::time_point getTimestamp() const = 0;/*** @brief 获取描述信息* * 提供人类可读的描述,便于用户识别不同的状态点* * @return std::string 描述信息*/virtual std::string getDescription() const = 0;
};
Caretaker(管理者)
职责:备忘录的生命周期管理
特征:
- 保存和管理备忘录对象
- 不操作或检查备忘录内容
- 提供历史记录管理功能
/*** @brief 管理者基类* * 负责备忘录的存储、检索和管理,但不了解备忘录的具体内容*/
class Caretaker {
protected:std::vector<std::unique_ptr<Memento>> history_;size_t maxHistorySize_;public:/*** @brief 保存备忘录* * 将备忘录添加到历史记录中,可能涉及历史记录清理* * @param memento 要保存的备忘录*/virtual void saveMemento(std::unique_ptr<Memento> memento) = 0;/*** @brief 获取最近的备忘录* * 从历史记录中检索最新的备忘录用于恢复* * @return std::unique_ptr<Memento> 最近的备忘录*/virtual std::unique_ptr<Memento> getLastMemento() = 0;/*** @brief 获取指定索引的备忘录* * 支持按索引访问历史记录中的任意备忘录* * @param index 备忘录索引* @return std::unique_ptr<Memento> 指定索引的备忘录*/virtual std::unique_ptr<Memento> getMemento(size_t index) = 0;
};
2.2 完整的UML架构图
classDiagramclass Originator {<<abstract>>#internalState: StateType+createMemento() Memento*+restoreFromMemento(memento: Memento*) void+performOperation() void+getState() StateType+setState(state: StateType) void}class Memento {<<abstract>>#timestamp: TimePoint#description: string+getTimestamp() TimePoint+getDescription() string}class ConcreteMemento {-savedState: StateType+ConcreteMemento(state: StateType, desc: string)+getState() StateType+getTimestamp() TimePoint+getDescription() string}class Caretaker {<<abstract>>#history: vector~Memento*~#maxSize: size_t+saveMemento(memento: Memento*) void+getLastMemento() Memento*+getMemento(index: size_t) Memento*+getHistorySize() size_t+clearHistory() void}class HistoryManager {-currentIndex: size_t+saveMemento(memento: Memento*) void+undo() Memento*+redo() Memento*+canUndo() bool+canRedo() bool+getHistoryList() vector~string~}class Client {+main()}Originator --> Memento : createsOriginator --> ConcreteMemento : usesCaretaker --> Memento : storesHistoryManager --> CaretakerClient --> OriginatorClient --> HistoryManagernote for Originator "创建备忘录时保存状态\n恢复时从备忘录读取状态"note for Memento "封装状态,保护内部数据\n只有Originator可以访问细节"note for Caretaker "管理备忘录生命周期\n不操作备忘录内容"
3. 设计哲学与实现考量
3.1 核心设计原则
封装性保护
备忘录模式的核心价值在于在保存状态的同时保护封装性。这是一个微妙但重要的平衡:
/*** @brief 文本编辑器备忘录 - 封装性实现示例* * 通过友元关系和私有构造函数确保只有Originator可以访问备忘录内部*/
class TextEditorMemento : public Memento {
private:// 私有数据,保护封装性std::string content_;TextFormat format_;CursorPosition cursorPos_;std::chrono::system_clock::time_point timestamp_;std::string description_;// 友元声明,只有TextEditor可以创建和访问备忘录friend class TextEditor;/*** @brief 私有构造函数* * 确保只有TextEditor可以创建备忘录实例* * @param content 文本内容* @param format 文本格式* @param cursor 光标位置* @param desc 描述信息*/TextEditorMemento(const std::string& content, const TextFormat& format,const CursorPosition& cursor,const std::string& desc): content_(content), format_(format), cursorPos_(cursor), description_(desc) {timestamp_ = std::chrono::system_clock::now();}public:// 公共接口只提供基本信息,不暴露内部状态细节std::chrono::system_clock::time_point getTimestamp() const override {return timestamp_;}std::string getDescription() const override {return description_;}private:// 只有TextEditor可以访问的私有方法std::string getContent() const { return content_; }TextFormat getFormat() const { return format_; }CursorPosition getCursorPosition() const { return cursorPos_; }
};
职责分离
模式成功的关键在于清晰的职责划分:
角色 | 职责 | 不应当负责 |
---|---|---|
Originator | 状态创建和恢复 | 状态存储管理 |
Memento | 状态安全存储 | 状态解释和操作 |
Caretaker | 历史记录管理 | 状态内容访问 |
3.2 实现权衡与决策
存储策略选择
完整存储 vs 增量存储:
/*** @brief 完整状态备忘录* * 保存对象的完整状态,实现简单但可能内存效率低*/
class FullStateMemento : public Memento {
private:CompleteObjectState fullState_;// 存储整个对象状态
};/*** @brief 增量状态备忘录* * 只保存发生变化的部分,内存效率高但实现复杂*/
class DeltaMemento : public Memento {
private:StateDifference delta_;std::shared_ptr<Memento> baseState_;// 只存储状态差异
};
权衡比较表:
方面 | 完整存储 | 增量存储 |
---|---|---|
实现复杂度 | 低 ✅ | 高 ❌ |
内存使用 | 高 ❌ | 低 ✅ |
恢复速度 | 快 ✅ | 慢 ❌ |
适用场景 | 小对象状态 | 大对象状态 |
生命周期管理
内存管理策略:
/*** @brief 智能备忘录管理器* * 使用智能指针自动管理备忘录生命周期,避免内存泄漏*/
class SmartCaretaker : public Caretaker {
private:std::vector<std::shared_ptr<Memento>> history_;size_t currentIndex_;size_t maxSize_;public:void saveMemento(std::shared_ptr<Memento> memento) override {// 清理当前索引之后的历史(重做分支)if (currentIndex_ < history_.size() - 1) {history_.resize(currentIndex_ + 1);}history_.push_back(memento);currentIndex_ = history_.size() - 1;// 限制历史记录大小if (history_.size() > maxSize_) {history_.erase(history_.begin());currentIndex_--;}}std::shared_ptr<Memento> undo() {if (currentIndex_ > 0) {currentIndex_--;return history_[currentIndex_];}return nullptr;}std::shared_ptr<Memento> redo() {if (currentIndex_ < history_.size() - 1) {currentIndex_++;return history_[currentIndex_];}return nullptr;}
};
3.3 性能优化策略
懒保存机制
/*** @brief 智能起源器 - 支持懒保存* * 只有在状态实际发生变化时才创建备忘录*/
class SmartOriginator {
private:StateType currentState_;StateType lastSavedState_;bool isDirty_;public:std::shared_ptr<Memento> createMementoIfNeeded() {if (isDirty_ && currentState_ != lastSavedState_) {auto memento = createMemento();lastSavedState_ = currentState_;isDirty_ = false;return memento;}return nullptr;}void markDirty() {isDirty_ = true;}
};
4. 综合实例分析
4.1 案例一:高级文本编辑器
需求分析:
- 支持多级撤销/重做
- 保存文本内容、格式和光标位置
- 支持保存点标记
- 内存使用优化
完整实现:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <chrono>
#include <stack>// 文本格式结构
struct TextFormat {std::string fontFamily;int fontSize;bool isBold;bool isItalic;std::string color;bool operator==(const TextFormat& other) const {return fontFamily == other.fontFamily &&fontSize == other.fontSize &&isBold == other.isBold &&isItalic == other.isItalic &&color == other.color;}
};// 光标位置结构
struct CursorPosition {size_t line;size_t column;size_t selectionStart;size_t selectionEnd;bool operator==(const CursorPosition& other) const {return line == other.line &&column == other.column &&selectionStart == other.selectionStart &&selectionEnd == other.selectionEnd;}
};// 文本编辑器状态
struct EditorState {std::string content;TextFormat format;CursorPosition cursor;std::string documentName;bool operator==(const EditorState& other) const {return content == other.content &&format == other.format &&cursor == other.cursor &&documentName == other.documentName;}bool operator!=(const EditorState& other) const {return !(*this == other);}
};/*** @brief 文本编辑器备忘录* * 保存文本编辑器的完整状态,包括内容、格式和光标位置*/
class TextEditorMemento : public Memento {
private:EditorState savedState_;std::chrono::system_clock::time_point timestamp_;std::string description_;friend class AdvancedTextEditor;TextEditorMemento(const EditorState& state, const std::string& desc): savedState_(state), description_(desc) {timestamp_ = std::chrono::system_clock::now();}public:std::chrono::system_clock::time_point getTimestamp() const override {return timestamp_;}std::string getDescription() const override {return description_;}private:// 只有AdvancedTextEditor可以访问EditorState getState() const {return savedState_;}
};/*** @brief 高级文本编辑器 - 起源器实现* * 支持丰富的文本编辑功能,可以创建和恢复备忘录*/
class AdvancedTextEditor {
private:EditorState currentState_;std::string lastOperation_;public:AdvancedTextEditor(const std::string& docName = "Untitled") {currentState_.documentName = docName;currentState_.content = "";currentState_.format = {"Arial", 12, false, false, "#000000"};currentState_.cursor = {0, 0, 0, 0};}/*** @brief 插入文本* * 在当前位置插入文本,更新光标位置* * @param text 要插入的文本*/void insertText(const std::string& text) {// 实际实现会处理文本插入和光标更新currentState_.content += text;updateCursorAfterInsert(text.length());lastOperation_ = "插入文本: " + (text.length() > 10 ? text.substr(0, 10) + "..." : text);}/*** @brief 删除文本* * 删除选定文本或光标前的内容*/void deleteText() {if (hasSelection()) {// 删除选定内容size_t start = currentState_.cursor.selectionStart;size_t end = currentState_.cursor.selectionEnd;currentState_.content.erase(start, end - start);currentState_.cursor.column = start;clearSelection();} else if (currentState_.cursor.column > 0) {// 删除光标前一个字符currentState_.content.erase(currentState_.cursor.column - 1, 1);currentState_.cursor.column--;}lastOperation_ = "删除文本";}/*** @brief 设置文本格式* * 应用指定的文本格式到选定内容或默认格式* * @param format 新的文本格式*/void setFormat(const TextFormat& format) {currentState_.format = format;lastOperation_ = "更改格式";}/*** @brief 创建备忘录* * 捕获当前编辑器的完整状态* * @return std::shared_ptr<TextEditorMemento> 状态备忘录*/std::shared_ptr<TextEditorMemento> createMemento() const {std::string desc = lastOperation_ + " @" + std::to_string(currentState_.content.length()) + "字符";return std::make_shared<TextEditorMemento>(currentState_, desc);}/*** @brief 从备忘录恢复* * 将编辑器状态恢复到备忘录保存时的状态* * @param memento 要恢复的备忘录*/void restoreFromMemento(std::shared_ptr<TextEditorMemento> memento) {currentState_ = memento->getState();lastOperation_ = "恢复状态";}/*** @brief 显示当前状态* * 输出编辑器的当前内容摘要*/void displayStatus() const {std::cout << "文档: " << currentState_.documentName << std::endl;std::cout << "内容: " << (currentState_.content.length() > 50 ? currentState_.content.substr(0, 50) + "..." : currentState_.content) << std::endl;std::cout << "长度: " << currentState_.content.length() << " 字符" << std::endl;std::cout << "光标: 行 " << currentState_.cursor.line << ", 列 " << currentState_.cursor.column << std::endl;if (hasSelection()) {std::cout << "选定: " << currentState_.cursor.selectionStart << " - " << currentState_.cursor.selectionEnd << std::endl;}std::cout << "---" << std::endl;}private:bool hasSelection() const {return currentState_.cursor.selectionStart != currentState_.cursor.selectionEnd;}void clearSelection() {currentState_.cursor.selectionStart = currentState_.cursor.selectionEnd;}void updateCursorAfterInsert(size_t length) {currentState_.cursor.column += length;clearSelection();}
};/*** @brief 高级历史管理器* * 支持撤销/重做操作,限制历史记录大小*/
class AdvancedHistoryManager {
private:std::vector<std::shared_ptr<TextEditorMemento>> undoStack_;std::vector<std::shared_ptr<TextEditorMemento>> redoStack_;size_t maxHistorySize_;size_t currentSavePoint_;public:AdvancedHistoryManager(size_t maxSize = 100) : maxHistorySize_(maxSize), currentSavePoint_(0) {}/*** @brief 保存状态* * 将状态保存到撤销栈,清空重做栈* * @param memento 要保存的备忘录*/void saveState(std::shared_ptr<TextEditorMemento> memento) {undoStack_.push_back(memento);redoStack_.clear(); // 新的操作清空重做历史// 限制历史记录大小if (undoStack_.size() > maxHistorySize_) {undoStack_.erase(undoStack_.begin());if (currentSavePoint_ > 0) currentSavePoint_--;}}/*** @brief 撤销操作* * 从撤销栈弹出状态,压入重做栈* * @return std::shared_ptr<TextEditorMemento> 要恢复的状态*/std::shared_ptr<TextEditorMemento> undo() {if (undoStack_.size() <= 1) return nullptr; // 需要至少2个状态才能撤销auto currentState = undoStack_.back();undoStack_.pop_back();redoStack_.push_back(currentState);return undoStack_.back(); // 返回前一个状态}/*** @brief 重做操作* * 从重做栈弹出状态,压入撤销栈* * @return std::shared_ptr<TextEditorMemento> 要恢复的状态*/std::shared_ptr<TextEditorMemento> redo() {if (redoStack_.empty()) return nullptr;auto state = redoStack_.back();redoStack_.pop_back();undoStack_.push_back(state);return state;}/*** @brief 设置保存点* * 标记当前状态为保存点,用于文档修改状态检测*/void setSavePoint() {currentSavePoint_ = undoStack_.size();}/*** @brief 检查是否有未保存的修改* * @return bool 是否有未保存的修改*/bool hasUnsavedChanges() const {return undoStack_.size() != currentSavePoint_;}/*** @brief 是否可以撤销* * @return bool 是否可以执行撤销*/bool canUndo() const {return undoStack_.size() > 1;}/*** @brief 是否可以重做* * @return bool 是否可以执行重做*/bool canRedo() const {return !redoStack_.empty();}/*** @brief 获取历史记录信息* * @return std::vector<std::string> 历史记录描述列表*/std::vector<std::string> getHistoryInfo() const {std::vector<std::string> info;for (const auto& memento : undoStack_) {info.push_back(memento->getDescription());}return info;}
};// 演示代码
int main() {std::cout << "🖊️ 高级文本编辑器演示" << std::endl;std::cout << "====================" << std::endl;// 创建编辑器和历史管理器AdvancedTextEditor editor("测试文档");AdvancedHistoryManager history(10); // 最多保存10个历史状态// 初始状态std::cout << "初始状态:" << std::endl;editor.displayStatus();history.saveState(editor.createMemento());// 一系列编辑操作std::cout << "执行编辑操作..." << std::endl;editor.insertText("你好,这是一个备忘录模式的演示。");history.saveState(editor.createMemento());editor.displayStatus();editor.insertText(" 我们正在测试撤销和重做功能。");history.saveState(editor.createMemento());editor.displayStatus();editor.insertText(" 这是第三段文本。");history.saveState(editor.createMemento());editor.displayStatus();// 执行撤销std::cout << "执行撤销操作..." << std::endl;if (history.canUndo()) {auto memento = history.undo();editor.restoreFromMemento(memento);editor.displayStatus();}// 再次撤销std::cout << "再次撤销..." << std::endl;if (history.canUndo()) {auto memento = history.undo();editor.restoreFromMemento(memento);editor.displayStatus();}// 执行重做std::cout << "执行重做..." << std::endl;if (history.canRedo()) {auto memento = history.redo();editor.restoreFromMemento(memento);editor.displayStatus();}// 显示历史信息std::cout << "历史记录:" << std::endl;auto historyInfo = history.getHistoryInfo();for (size_t i = 0; i < historyInfo.size(); ++i) {std::cout << i << ": " << historyInfo[i] << std::endl;}return 0;
}
时序图分析:
4.2 案例二:游戏存档系统
需求分析:
- 支持多个存档槽位
- 保存玩家状态、游戏进度、物品栏
- 快速保存/加载功能
- 存档元数据管理
完整实现:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <chrono>// 游戏角色状态
struct PlayerState {std::string name;int level;int experience;int health;int maxHealth;int mana;int maxMana;std::string currentLocation;std::vector<std::string> inventory;std::map<std::string, int> skills;void display() const {std::cout << "角色: " << name << " (Lv." << level << ")" << std::endl;std::cout << "生命: " << health << "/" << maxHealth;std::cout << " 魔法: " << mana << "/" << maxMana << std::endl;std::cout << "经验: " << experience << std::endl;std::cout << "位置: " << currentLocation << std::endl;std::cout << "物品栏: ";for (const auto& item : inventory) {std::cout << item << " ";}std::cout << std::endl;}
};// 游戏世界状态
struct WorldState {std::map<std::string, bool> completedQuests;std::map<std::string, bool> discoveredLocations;std::chrono::system_clock::time_point gameTime;int totalPlayTime; // 秒void display() const {auto timeT = std::chrono::system_clock::to_time_t(gameTime);std::cout << "游戏时间: " << std::ctime(&timeT);std::cout << "总游戏时间: " << totalPlayTime / 3600 << "小时" << (totalPlayTime % 3600) / 60 << "分钟" << std::endl;std::cout << "完成任务: " << completedQuests.size() << "个" << std::endl;}
};/*** @brief 游戏存档备忘录* * 保存完整的游戏状态,包括玩家和世界状态*/
class GameSaveMemento : public Memento {
private:PlayerState playerState_;WorldState worldState_;std::string saveName_;std::chrono::system_clock::time_point saveTime_;std::string description_;friend class GameSession;GameSaveMemento(const PlayerState& player, const WorldState& world,const std::string& name, const std::string& desc): playerState_(player), worldState_(world), saveName_(name), description_(desc) {saveTime_ = std::chrono::system_clock::now();}public:std::chrono::system_clock::time_point getTimestamp() const override {return saveTime_;}std::string getDescription() const override {return description_;}std::string getSaveName() const {return saveName_;}private:PlayerState getPlayerState() const { return playerState_; }WorldState getWorldState() const { return worldState_; }
};/*** @brief 游戏会话 - 起源器实现* * 管理游戏状态,支持存档和读档*/
class GameSession {
private:PlayerState currentPlayer_;WorldState currentWorld_;std::chrono::system_clock::time_point sessionStart_;public:GameSession(const std::string& playerName) {currentPlayer_.name = playerName;currentPlayer_.level = 1;currentPlayer_.experience = 0;currentPlayer_.health = 100;currentPlayer_.maxHealth = 100;currentPlayer_.mana = 50;currentPlayer_.maxMana = 50;currentPlayer_.currentLocation = "起始村庄";currentPlayer_.inventory = {"新手剑", "治疗药水"};currentPlayer_.skills = {{"攻击", 1}, {"防御", 1}};currentWorld_.gameTime = std::chrono::system_clock::now();currentWorld_.totalPlayTime = 0;currentWorld_.completedQuests = {{"新手教程", true}};currentWorld_.discoveredLocations = {{"起始村庄", true}};sessionStart_ = std::chrono::system_clock::now();}/*** @brief 玩家升级*/void levelUp() {currentPlayer_.level++;currentPlayer_.maxHealth += 20;currentPlayer_.health = currentPlayer_.maxHealth;currentPlayer_.maxMana += 10;currentPlayer_.mana = currentPlayer_.maxMana;std::cout << "🎉 " << currentPlayer_.name << " 升级到 " << currentPlayer_.level << " 级!" << std::endl;}/*** @brief 获得经验*/void gainExperience(int exp) {currentPlayer_.experience += exp;std::cout << "获得 " << exp << " 点经验" << std::endl;}/*** @brief 添加物品*/void addItem(const std::string& item) {currentPlayer_.inventory.push_back(item);std::cout << "获得物品: " << item << std::endl;}/*** @brief 移动位置*/void moveTo(const std::string& location) {currentPlayer_.currentLocation = location;currentWorld_.discoveredLocations[location] = true;std::cout << "移动到: " << location << std::endl;}/*** @brief 完成任务*/void completeQuest(const std::string& questName) {currentWorld_.completedQuests[questName] = true;std::cout << "完成任务: " << questName << std::endl;}/*** @brief 创建存档*/std::shared_ptr<GameSaveMemento> createSave(const std::string& saveName) {updatePlayTime();std::string desc = currentPlayer_.name + " Lv." + std::to_string(currentPlayer_.level) + " - " +currentPlayer_.currentLocation;return std::make_shared<GameSaveMemento>(currentPlayer_, currentWorld_, saveName, desc);}/*** @brief 从存档恢复*/void loadFromSave(std::shared_ptr<GameSaveMemento> save) {currentPlayer_ = save->getPlayerState();currentWorld_ = save->getWorldState();sessionStart_ = std::chrono::system_clock::now();std::cout << "已加载存档: " << save->getSaveName() << std::endl;}/*** @brief 显示当前状态*/void displayStatus() const {std::cout << "\n=== 当前游戏状态 ===" << std::endl;currentPlayer_.display();currentWorld_.display();std::cout << "===================" << std::endl;}private:void updatePlayTime() {auto now = std::chrono::system_clock::now();auto sessionDuration = std::chrono::duration_cast<std::chrono::seconds>(now - sessionStart_);currentWorld_.totalPlayTime += sessionDuration.count();sessionStart_ = now;}
};/*** @brief 存档管理器* * 管理多个存档槽位,支持快速保存和加载*/
class SaveManager {
private:std::map<std::string, std::shared_ptr<GameSaveMemento>> saveSlots_;std::shared_ptr<GameSaveMemento> quickSave_;public:/*** @brief 保存到指定槽位*/void saveToSlot(const std::string& slotName, std::shared_ptr<GameSaveMemento> save) {saveSlots_[slotName] = save;std::cout << "💾 已保存到槽位: " << slotName << std::endl;}/*** @brief 从槽位加载*/std::shared_ptr<GameSaveMemento> loadFromSlot(const std::string& slotName) {auto it = saveSlots_.find(slotName);if (it != saveSlots_.end()) {std::cout << "📂 从槽位加载: " << slotName << std::endl;return it->second;}std::cout << "❌ 槽位不存在: " << slotName << std::endl;return nullptr;}/*** @brief 快速保存*/void quickSave(std::shared_ptr<GameSaveMemento> save) {quickSave_ = save;std::cout << "⚡ 快速保存完成" << std::endl;}/*** @brief 快速加载*/std::shared_ptr<GameSaveMemento> quickLoad() {if (quickSave_) {std::cout << "⚡ 快速加载完成" << std::endl;return quickSave_;}std::cout << "❌ 没有快速存档" << std::endl;return nullptr;}/*** @brief 列出所有存档*/void listSaves() const {std::cout << "\n=== 存档列表 ===" << std::endl;for (const auto& pair : saveSlots_) {std::cout << "槽位: " << pair.first << " - " << pair.second->getDescription() << std::endl;}if (quickSave_) {std::cout << "快速存档: " << quickSave_->getDescription() << std::endl;}std::cout << "================" << std::endl;}
};// 演示代码
int main() {std::cout << "🎮 游戏存档系统演示" << std::endl;std::cout << "===================" << std::endl;// 创建游戏会话和存档管理器GameSession game("冒险者");SaveManager saveManager;// 显示初始状态game.displayStatus();// 进行一些游戏操作std::cout << "\n--- 游戏进程 ---" << std::endl;game.gainExperience(100);game.levelUp();game.addItem("魔法杖");game.moveTo("黑暗森林");game.completeQuest("森林探险");game.displayStatus();// 保存游戏std::cout << "\n--- 存档操作 ---" << std::endl;auto save1 = game.createSave("存档1");saveManager.saveToSlot("slot1", save1);// 继续游戏std::cout << "\n--- 继续游戏 ---" << std::endl;game.gainExperience(50);game.addItem("稀有宝石");game.moveTo("龙之巢穴");game.displayStatus();// 快速保存auto quickSave = game.createSave("快速存档");saveManager.quickSave(quickSave);// 列出所有存档saveManager.listSaves();// 从槽位加载std::cout << "\n--- 读档操作 ---" << std::endl;auto loadedSave = saveManager.loadFromSlot("slot1");if (loadedSave) {game.loadFromSave(loadedSave);game.displayStatus();}return 0;
}
4.3 Makefile构建配置
# 编译器设置
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -O2 -g
TARGET := memento_demo
SRCS := text_editor.cpp game_save.cpp main.cpp# 默认目标
all: $(TARGET)# 链接目标文件
$(TARGET): $(SRCS:.cpp=.o)$(CXX) $(CXXFLAGS) -o $@ $^# 编译源文件
%.o: %.cpp$(CXX) $(CXXFLAGS) -c $< -o $@# 清理生成文件
clean:rm -f *.o $(TARGET)# 运行演示
run: $(TARGET)./$(TARGET)# 调试编译
debug: CXXFLAGS += -DDEBUG -Og
debug: $(TARGET)# 发布编译
release: CXXFLAGS += -DNDEBUG -O3
release: clean $(TARGET)# 内存检查
memcheck: $(TARGET)valgrind --leak-check=full ./$(TARGET)# 性能分析
profile: CXXFLAGS += -pg
profile: clean $(TARGET)./$(TARGET)gprof $(TARGET) gmon.out > analysis.txt.PHONY: all clean run debug release memcheck profile
4.4 编译与运行指南
编译方法:
# 1. 基础编译
make# 2. 调试模式(包含调试信息)
make debug# 3. 发布模式(优化性能)
make release# 4. 内存检查
make memcheck# 5. 性能分析
make profile
运行结果解读:
🖊️ 高级文本编辑器演示
====================
初始状态:
文档: 测试文档
内容:
长度: 0 字符
光标: 行 0, 列 0
---
执行编辑操作...
文档: 测试文档
内容: 你好,这是一个备忘录模式的演示。
长度: 24 字符
光标: 行 0, 列 24
---
执行撤销操作...
文档: 测试文档
内容: 你好,这是一个备忘录模式的演示。
长度: 24 字符
光标: 行 0, 列 24
---
历史记录:
0: 插入文本: 你好,这是一个备忘录模式的演示。 @24字符
1: 插入文本: 我们正在测试撤销和重做功能。 @47字符
结果分析:
- ✅ 成功实现了多级撤销/重做功能
- ✅ 状态保存和恢复正确工作
- ✅ 历史记录管理有效
- ✅ 封装性得到保护
5. 高级主题与最佳实践
5.1 性能优化策略
增量状态保存
/*** @brief 增量备忘录* * 只保存状态变化的部分,减少内存使用*/
class DeltaMemento : public Memento {
private:std::shared_ptr<Memento> baseState_;StateDifference delta_;std::chrono::system_clock::time_point timestamp_;public:DeltaMemento(std::shared_ptr<Memento> base, const StateDifference& delta): baseState_(base), delta_(delta) {timestamp_ = std::chrono::system_clock::now();}// 应用差异到基础状态来重建完整状态CompleteState reconstructState() const {auto base = baseState_->getState();return applyDelta(base, delta_);}
};
压缩存储
/*** @brief 压缩备忘录* * 使用压缩算法减少存储空间*/
class CompressedMemento : public Memento {
private:std::vector<uint8_t> compressedData_;std::vector<uint8_t> compress(const CompleteState& state) {// 使用zlib或其他压缩库return compressData(serializeState(state));}CompleteState decompress() const {return deserializeState(decompressData(compressedData_));}
};
5.2 分布式系统应用
在微服务架构中,备忘录模式可以用于实现分布式事务的补偿操作:
/*** @brief 分布式事务备忘录* * 保存分布式操作的状态,支持补偿事务*/
class DistributedTransactionMemento : public Memento {
private:struct ServiceOperation {std::string serviceName;std::string operation;std::string parameters;std::string compensation;bool completed;};std::vector<ServiceOperation> operations_;std::string transactionId_;public:void addOperation(const std::string& service, const std::string& op,const std::string& params, const std::string& comp) {operations_.push_back({service, op, params, comp, false});}void markCompleted(size_t index) {if (index < operations_.size()) {operations_[index].completed = true;}}// 执行补偿操作回滚事务void compensate() {for (auto it = operations_.rbegin(); it != operations_.rend(); ++it) {if (it->completed) {executeCompensation(it->serviceName, it->compensation);}}}
};
6. 模式对比与选择指南
6.1 相关模式对比
模式 | 目的 | 与备忘录模式的关系 |
---|---|---|
命令模式 | 封装操作请求 | 命令模式可以结合备忘录实现可撤销的操作 |
原型模式 | 通过克隆创建对象 | 原型模式克隆整个对象,备忘录只保存状态 |
状态模式 | 封装状态相关行为 | 状态模式关注行为,备忘录模式关注状态保存 |
6.2 选择决策树
是否需要保存对象状态?├── 是 → 状态是否简单?│ ├── 是 → 考虑直接序列化│ └── 否 → 需要保护封装性?│ ├── 是 → 使用备忘录模式 ✅│ └── 否 → 考虑其他方法└── 否 → 不需要备忘录模式
7. 总结
备忘录模式是一个强大而优雅的设计模式,它完美地解决了状态保存与封装性保护之间的矛盾。通过本文的深度解析,我们可以看到:
核心价值
- 🛡️ 封装性保护:在不暴露内部细节的前提下保存状态
- 🔄 状态管理:提供完整的状态保存和恢复机制
- 🎯 职责清晰:明确的角色分离和职责划分
实现要点
- 合理使用友元关系保护备忘录内容
- 选择适当的状态存储策略(完整/增量)
- 实现健壮的历史记录管理
- 考虑性能和内存使用优化
适用场景
- 文本编辑器的撤销/重做功能
- 游戏存档系统
- 事务回滚机制
- 配置保存和恢复
- 任何需要"时间旅行"功能的系统
备忘录模式不仅是技术实现,更是一种设计哲学的体现:在复杂系统中为用户提供"后悔"的权利,让交互更加友好和可靠。掌握这个模式,你将能够构建出更加健壮和用户友好的软件系统。