设计模式(C++)详解——备忘录模式(1) - 实践

备忘录模式:时光倒流的魔法秘籍 ✨

想象一下,如果生活中有一个"撤销"按钮该多好!备忘录模式就是编程世界里的时光机,让我们一起来探索这个神奇的设计模式吧!

模式初印象:什么是备忘录?

备忘录模式就像是我们生活中的后悔药 !它允许我们在不暴露对象内部细节的情况下,保存和恢复对象的状态。

核心思想:偷偷拍下对象的"快照" ,等到需要的时候再恢复到那个美好的瞬间!

// 举个栗子:文本编辑器的撤销功能
你输入:Hello World!
然后删除,变成了:Hello
突然想:"哎呀,我为什么要删掉World!"
点击撤销 → 回到:Hello World!

这就是备忘录模式的魔力!

️ 三大主角登场

1. Originator(发起人)

角色:需要被保存状态的对象
任务:创建备忘录 + 从备忘录恢复

2. Memento(备忘录)

角色:状态存储箱
任务:安全地保存发起人的内部状态

3. Caretaker(管理者)

角色:备忘录的保管员
任务:保存和管理备忘录,但不能查看或操作内容

深入原理:如何实现时光倒流?

UML类图大揭秘

creates
stores
Originator
-state: string
+createMemento()
+restore(m: Memento)
+setState(s: string)
+getState()
Memento
-state: string
+Memento(s: string)
+getState()
Caretaker
-mementos: vector<Memento>
+saveMemento(m: Memento)
+getMemento(index: int)

C++实现:文本编辑器案例

#include <iostream>
  #include <string>
    #include <vector>
      #include <memory>
        /**
        * @brief 备忘录类 - 时光胶囊
        *
        * 存储文本编辑器的状态,就像给文本拍了一张照片
        * 注意:为了封装性,我们使用友元类,但要谨慎使用!
        */
        class TextMemento {
        private:
        std::string content_;  // 保存的文本内容
        // 只有TextEditor可以创建和访问备忘录
        friend class TextEditor;
        /**
        * @brief 构造函数 - 保存状态
        *
        * @param content 要保存的文本内容
        */
        TextMemento(const std::string& content) : content_(content) {}
        /**
        * @brief 获取保存的状态
        *
        * @return std::string 保存的文本内容
        */
        std::string getContent() const {
        return content_;
        }
        };
        /**
        * @brief 发起人类 - 文本编辑器
        *
        * 可以创建备忘录保存当前状态,也可以从备忘录恢复状态
        */
        class TextEditor {
        private:
        std::string content_;  // 当前文本内容
        public:
        /**
        * @brief 设置文本内容
        *
        * @param text 新的文本内容
        */
        void write(const std::string& text) {
        content_ = text;
        std::cout << " 当前文本: " << content_ << std::endl;
        }
        /**
        * @brief 获取当前文本
        *
        * @return std::string 当前文本内容
        */
        std::string getContent() const {
        return content_;
        }
        /**
        * @brief 创建备忘录 - 拍快照!
        *
        * @return std::shared_ptr<TextMemento> 包含当前状态的备忘录
          */
          std::shared_ptr<TextMemento> save() {
            std::cout << " 保存状态: " << content_ << std::endl;
            return std::make_shared<TextMemento>(content_);
              }
              /**
              * @brief 从备忘录恢复 - 时光倒流!
              *
              * @param memento 要恢复的备忘录
              */
              void restore(std::shared_ptr<TextMemento> memento) {
                content_ = memento->getContent();
                std::cout << " 恢复状态: " << content_ << std::endl;
                }
                };
                /**
                * @brief 管理者类 - 历史记录管理器
                *
                * 负责保存和管理所有的备忘录,但不能查看内容
                * 就像是一个安全的保险箱 
                */
                class HistoryManager {
                private:
                std::vector<std::shared_ptr<TextMemento>> history_;  // 历史记录栈
                  public:
                  /**
                  * @brief 保存备忘录到历史记录
                  *
                  * @param memento 要保存的备忘录
                  */
                  void push(std::shared_ptr<TextMemento> memento) {
                    history_.push_back(memento);
                    std::cout << " 已保存到历史记录,当前共有 "
                    << history_.size() << " 个记录" << std::endl;
                    }
                    /**
                    * @brief 从历史记录中弹出最新的备忘录
                    *
                    * @return std::shared_ptr<TextMemento> 最新的备忘录
                      */
                      std::shared_ptr<TextMemento> pop() {
                        if (history_.empty()) {
                        std::cout << "❌ 历史记录为空!" << std::endl;
                        return nullptr;
                        }
                        auto memento = history_.back();
                        history_.pop_back();
                        std::cout << "⏮️ 从历史记录恢复,剩余 "
                        << history_.size() << " 个记录" << std::endl;
                        return memento;
                        }
                        /**
                        * @brief 检查是否有历史记录
                        *
                        * @return bool 是否有记录
                        */
                        bool hasHistory() const {
                        return !history_.empty();
                        }
                        };
                        // 演示代码:让我们看看备忘录模式的威力!
                        int main() {
                        std::cout << " 开始文本编辑器演示..." << std::endl;
                        // 创建文本编辑器和历史管理器
                        TextEditor editor;
                        HistoryManager history;
                        // 第一次编辑并保存
                        std::cout << "\n--- 第一次编辑 ---" << std::endl;
                        editor.write("Hello, ");
                        history.push(editor.save());
                        // 第二次编辑并保存
                        std::cout << "\n--- 第二次编辑 ---" << std::endl;
                        editor.write("Hello, World!");
                        history.push(editor.save());
                        // 第三次编辑(不保存)
                        std::cout << "\n--- 第三次编辑(错误的编辑) ---" << std::endl;
                        editor.write("Hello, Wrold!");  // 拼写错误!
                        // 哎呀,拼写错了!撤销到上一个状态
                        std::cout << "\n--- 第一次撤销 ---" << std::endl;
                        auto lastState = history.pop();
                        if (lastState) {
                        editor.restore(lastState);
                        }
                        // 再撤销一次
                        std::cout << "\n--- 第二次撤销 ---" << std::endl;
                        lastState = history.pop();
                        if (lastState) {
                        editor.restore(lastState);
                        }
                        // 尝试再次撤销(应该没有记录了)
                        std::cout << "\n--- 尝试第三次撤销 ---" << std::endl;
                        lastState = history.pop();
                        if (!lastState) {
                        std::cout << "无法撤销,已经没有历史记录了!" << std::endl;
                        }
                        return 0;
                        }

更多精彩案例

案例1:游戏存档系统

#include <iostream>
  #include <vector>
    #include <memory>
      /**
      * @brief 游戏角色备忘录
      */
      class GameMemento {
      private:
      int level_;
      int health_;
      std::string position_;
      friend class GameCharacter;
      GameMemento(int level, int health, const std::string& pos)
      : level_(level), health_(health), position_(pos) {}
      int getLevel() const { return level_; }
      int getHealth() const { return health_; }
      std::string getPosition() const { return position_; }
      };
      /**
      * @brief 游戏角色
      */
      class GameCharacter {
      private:
      int level_ = 1;
      int health_ = 100;
      std::string position_ = "起始点";
      public:
      void levelUp() {
      level_++;
      health_ = 100;  // 升级回满血
      std::cout << " 升级到 " << level_ << " 级!" << std::endl;
      }
      void takeDamage(int damage) {
      health_ -= damage;
      std::cout << " 受到 " << damage << " 点伤害,剩余生命: " << health_ << std::endl;
      }
      void moveTo(const std::string& position) {
      position_ = position;
      std::cout << " 移动到: " << position_ << std::endl;
      }
      void displayStatus() const {
      std::cout << " 状态 - 等级:" << level_
      << " 生命:" << health_
      << " 位置:" << position_ << std::endl;
      }
      std::shared_ptr<GameMemento> save() {
        std::cout << " 游戏已存档" << std::endl;
        return std::make_shared<GameMemento>(level_, health_, position_);
          }
          void load(std::shared_ptr<GameMemento> memento) {
            level_ = memento->getLevel();
            health_ = memento->getHealth();
            position_ = memento->getPosition();
            std::cout << " 游戏已读档" << std::endl;
            displayStatus();
            }
            };
            // 游戏存档管理器
            class SaveManager {
            private:
            std::vector<std::shared_ptr<GameMemento>> saves_;
              public:
              void quickSave(std::shared_ptr<GameMemento> save) {
                saves_.push_back(save);
                std::cout << "⚡ 快速存档完成" << std::endl;
                }
                std::shared_ptr<GameMemento> quickLoad() {
                  if (saves_.empty()) {
                  std::cout << "❌ 没有存档!" << std::endl;
                  return nullptr;
                  }
                  auto save = saves_.back();
                  std::cout << "⚡ 快速读档完成" << std::endl;
                  return save;
                  }
                  };

案例2:绘图程序的历史记录

#include <iostream>
  #include <vector>
    #include <memory>
      /**
      * @brief 绘图状态备忘录
      */
      class DrawingMemento {
      private:
      std::vector<std::string> shapes_;
        friend class DrawingCanvas;
        DrawingMemento(const std::vector<std::string>& shapes)
          : shapes_(shapes) {}
          std::vector<std::string> getShapes() const {
            return shapes_;
            }
            };
            /**
            * @brief 绘图画布
            */
            class DrawingCanvas {
            private:
            std::vector<std::string> shapes_;
              public:
              void drawShape(const std::string& shape) {
              shapes_.push_back(shape);
              std::cout << "✏️ 绘制: " << shape << std::endl;
              displayCanvas();
              }
              void displayCanvas() const {
              std::cout << " 画布内容: ";
              for (const auto& shape : shapes_) {
              std::cout << shape << " ";
              }
              std::cout << std::endl;
              }
              std::shared_ptr<DrawingMemento> save() {
                return std::make_shared<DrawingMemento>(shapes_);
                  }
                  void restore(std::shared_ptr<DrawingMemento> memento) {
                    shapes_ = memento->getShapes();
                    std::cout << " 恢复画布状态" << std::endl;
                    displayCanvas();
                    }
                    };

实现要点与技巧

封装性的保护神

备忘录模式最大的挑战就是如何在不破坏封装性的前提下保存状态。我们有几种解决方案:

方法优点缺点适用场景
友元类简单直接破坏封装性小型项目
内部类较好的封装C++支持有限推荐使用
接口隔离完全封装实现复杂大型项目

性能考虑 ⚡

内存使用:备忘录会占用额外内存,特别是状态很大时

// 优化技巧:增量保存
class IncrementalMemento {
// 只保存变化的部分,而不是整个状态
};

深拷贝 vs 浅拷贝

// 深拷贝 - 安全但耗时
TextMemento(const std::string& content) : content_(content) {}
// 如果需要优化,可以考虑共享不变的部分

适用场景大盘点

✅ 强烈推荐使用

  1. 撤销/重做功能

    • 文本编辑器、绘图软件、IDE
  2. 游戏存档

    • 角色状态、游戏进度、场景状态
  3. 事务回滚

    • 数据库操作、金融交易
  4. 配置保存 ⚙️

    • 软件设置、用户偏好

❌ 谨慎使用的情况

  1. 状态太大

    • 如果对象状态非常庞大,频繁保存可能内存爆炸
  2. 性能敏感 ⏱️

    • 实时系统、高频交易
  3. 简单场景

    • 如果用不上撤销功能,就别过度设计

与其他模式的对比

模式关系区别
命令模式好搭档命令模式记录操作,备忘录模式记录状态
原型模式类似原型模式克隆整个对象,备忘录只保存状态
状态模式互补状态模式管理行为,备忘录模式保存状态

最佳实践总结

  1. 封装性是王道

    • 确保只有Originator能访问Memento的内部
  2. 考虑内存使用

    • 大状态对象考虑增量保存或压缩
  3. 管理生命周期

    • 及时清理不再需要的备忘录
  4. 提供友好接口

    • 让Caretaker容易使用,但看不到内部

总结

备忘录模式就像编程世界里的时间魔法 ⏰!它让我们能够:

  • ✅ 保存对象状态而不破坏封装
  • ✅ 实现强大的撤销/重做功能
  • ✅ 支持事务回滚和存档功能
  • ✅ 让代码更加灵活和健壮

记住这个模式的精髓:偷偷保存,安全恢复!就像玩游戏时及时存档,遇到困难时从容读档一样 。

下次当你需要"撤销"功能时,别忘了备忘录模式这个得力助手!它会让你的程序像有了超能力一样强大 !


备忘录模式口诀

状态保存要封装,备忘录来帮忙忙
发起人,管理者,各司其职不越界
撤销重做真方便,程序健壮又灵活!

posted @ 2025-10-21 11:04  yxysuanfa  阅读(5)  评论(0)    收藏  举报