什么是备忘录模式?

备忘录模式是一种行为型设计模式,它的主要目标是:在不破坏封装的前提下,捕获一个对象的内部状态,并在以后需要时恢复到该状态。

换句话说,备忘录模式允许你保存对象的快照,并在需要时恢复它。


通俗解释

想象一个文本编辑器有“保存”和“撤销”功能:

  1. 当用户点击“保存”时,编辑器会保存当前的文本内容和光标位置。
  2. 如果用户点击“撤销”,编辑器会恢复到上一次保存的状态。

在备忘录模式中:

  • 编辑器是“发起人”对象,负责保存和恢复状态。
  • 备忘录存储状态的快照。
  • 管理者负责存储和管理备忘录(如保存历史记录)。

备忘录模式的特点

核心思想

将对象的状态保存到一个独立的备忘录对象中,从而可以在未来恢复状态。

优点
  1. 状态恢复:允许对象回滚到以前的状态。
  2. 封装性:备忘录对象独立于发起人,外界无法直接访问发起人的内部状态。
  3. 简化复杂操作:撤销/重做功能的实现变得非常直观。
缺点
  1. 内存开销:每次保存状态都会创建备忘录,可能占用大量内存。
  2. 实现复杂性:如果需要频繁保存复杂对象的状态,备忘录的管理会变得困难。

备忘录模式的结构

UML 类图
      +---------------------+
      |    Originator       |  // 发起人
      +---------------------+
      | - state: string     |  // 状态
      | + saveState()       |  // 保存状态
      | + restoreState(m)   |  // 恢复状态
      +---------------------+
                 ^
                 |
      +---------------------+
      |    Memento          |  // 备忘录
      +---------------------+
      | + getState()        |
      +---------------------+
                 ^
                 |
      +---------------------+
      |    Caretaker        |  // 管理者
      +---------------------+
      | - mementos: list    |  // 管理备忘录
      | + addMemento(m)     |
      | + getMemento(index) |
      +---------------------+

角色说明

  1. Originator(发起人):

    • 负责创建备忘录和恢复状态。
    • 保存的状态可以是任何需要回滚的对象属性。
  2. Memento(备忘录):

    • 存储发起人对象的状态快照。
    • 提供一个接口,让发起人可以恢复到以前的状态。
  3. Caretaker(管理者):

    • 负责存储和管理备忘录。
    • 不直接操作备忘录的内容,只负责存储和检索备忘录。

备忘录模式的优缺点

优点
  1. 解耦:发起人和管理者相互独立,符合单一职责原则。
  2. 恢复状态:可以方便地实现撤销/重做功能。
  3. 封装性:备忘录封装了状态,外部无法访问。
缺点
  1. 内存开销大:每次保存状态都会占用内存。
  2. 实现复杂:需要维护备忘录列表,并确保备忘录的完整性。

案例:文本编辑器的“保存”和“撤销”功能

需求描述

设计一个简单的文本编辑器,支持以下功能:

  1. 保存当前的文本内容(快照)。
  2. 撤销到之前保存的状态。

完整代码实现

以下是备忘录模式在文本编辑器场景中的应用。

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 备忘录类:存储文本状态
class Memento {
private:
    std::string state; // 保存的文本内容

public:
    explicit Memento(const std::string& state) : state(state) {}

    std::string getState() const {
        return state; // 返回保存的文本状态
    }
};

// 发起人类:文本编辑器
class TextEditor {
private:
    std::string text; // 当前文本内容

public:
    void appendText(const std::string& newText) {
        text += newText; // 追加文本内容
    }

    void showText() const {
        std::cout << "当前文本内容: " << text << std::endl;
    }

    // 创建备忘录(保存当前状态)
    std::shared_ptr<Memento> saveState() {
        return std::make_shared<Memento>(text);
    }

    // 从备忘录恢复状态
    void restoreState(const std::shared_ptr<Memento>& memento) {
        text = memento->getState();
    }
};

// 管理者类:负责管理备忘录
class Caretaker {
private:
    std::vector<std::shared_ptr<Memento>> history; // 备忘录历史记录

public:
    void addMemento(const std::shared_ptr<Memento>& memento) {
        history.push_back(memento); // 添加备忘录
    }

    std::shared_ptr<Memento> getMemento(size_t index) const {
        if (index < history.size()) {
            return history[index];
        }
        return nullptr; // 返回空指针表示无效索引
    }

    size_t getHistorySize() const {
        return history.size();
    }
};

// 客户端代码
int main() {
    // 创建文本编辑器和管理者
    TextEditor editor;
    Caretaker caretaker;

    // 第一次输入文本
    editor.appendText("Hello, ");
    editor.showText();
    caretaker.addMemento(editor.saveState()); // 保存状态

    // 第二次输入文本
    editor.appendText("world!");
    editor.showText();
    caretaker.addMemento(editor.saveState()); // 保存状态

    // 第三次输入文本
    editor.appendText(" How are you?");
    editor.showText();

    // 撤销到第二次保存的状态
    std::cout << "\n撤销到第二次保存的状态:" << std::endl;
    editor.restoreState(caretaker.getMemento(1));
    editor.showText();

    // 撤销到第一次保存的状态
    std::cout << "\n撤销到第一次保存的状态:" << std::endl;
    editor.restoreState(caretaker.getMemento(0));
    editor.showText();

    return 0;
}

运行结果

当前文本内容: Hello, 
当前文本内容: Hello, world!
当前文本内容: Hello, world! How are you?

撤销到第二次保存的状态:
当前文本内容: Hello, world!

撤销到第一次保存的状态:
当前文本内容: Hello, 

代码解析

1. 备忘录类(Memento)
class Memento {
private:
    std::string state; // 保存的文本内容

public:
    explicit Memento(const std::string& state) : state(state) {}

    std::string getState() const {
        return state; // 返回保存的文本状态
    }
};
  • 存储编辑器的文本内容(状态)。
  • 提供一个getState()方法,用于发起人恢复状态。

2. 发起人类(TextEditor)
class TextEditor {
private:
    std::string text; // 当前文本内容

public:
    void appendText(const std::string& newText) {
        text += newText; // 追加文本内容
    }

    void showText() const {
        std::cout << "当前文本内容: " << text << std::endl;
    }

    std::shared_ptr<Memento> saveState() {
        return std::make_shared<Memento>(text); // 保存当前状态到备忘录
    }

    void restoreState(const std::shared_ptr<Memento>& memento) {
        text = memento->getState(); // 恢复状态
    }
};
  • saveState():创建一个包含当前文本状态的备忘录。
  • restoreState():从备忘录恢复状态。

3. 管理者类(Caretaker)
class Caretaker {
private:
    std::vector<std::shared_ptr<Memento>> history; // 备忘录历史记录

public:
    void addMemento(const std::shared_ptr<Memento>& memento) {
        history.push_back(memento); // 添加备忘录
    }

    std::shared_ptr<Memento> getMemento(size_t index) const {
        if (index < history.size()) {
            return history[index];
        }
        return nullptr; // 无效索引返回空指针
    }

    size_t getHistorySize() const {
        return history.size();
    }
};
  • 负责管理备忘录的存储和检索。
  • addMemento()保存备忘录,getMemento()获取备忘录。

备忘录模式的适用场景

  1. 实现撤销/重做功能:
    • 如文本编辑器、图像编辑器。
  2. 保存复杂对象的快照:
    • 如游戏的存档功能。
  3. 需要恢复对象的状态:
    • 如事务回滚操作。

总结

备忘录模式的核心优势
  1. 封装性强:备忘录封装了状态,外界无法直接修改状态。
  2. 功能独立:发起人和管理者职责分明,逻辑清晰。
  3. 恢复机制简单:通过备忘录可以方便地恢复状态。
注意事项
  • 在保存复杂对象状态时,可能会消耗大量内存。
  • 需要合理设计管理者的备忘录存储机制,以避免内存泄漏或性能问题。

通过本案例,我们展示了备忘录模式如何实现文本编辑器的“保存”和“撤销”功能。备忘录模式非常适合需要状态保存和恢复的场景,如撤销操作、事务回滚和游戏存档!

更多推荐