C++软件设计模式之备忘录模式

备忘录模式(Memento Pattern)

目的意图

备忘录模式是一种行为设计模式,目的是在不破坏封装性的前提下,捕获并保存一个对象的内部状态,以便在之后可以将对象恢复到这个状态。备忘录模式的核心思想是将对象的状态保存到外部,这样对象可以在不暴露其内部结构的情况下被恢复。

关键角色

  1. Originator(发起人)

    • 这是需要保存和恢复状态的对象。
    • 它创建一个备忘录(Memento)对象来存储其内部状态。
    • 它可以从备忘录中恢复其状态。
  2. Memento(备忘录)

    • 这是存储 Originator 内部状态的对象。
    • 它通常是不可变对象,防止状态被意外修改。
  3. Caretaker(管理者)

    • 这是负责保存和恢复备忘录的对象。
    • 它不直接操作备忘录的内部状态,而是通过 Originator 来管理备忘录。

适用场合

备忘录模式适用于以下场景:

  1. 需要保存对象的内部状态

    • 当需要保存对象的内部状态,并在之后恢复到该状态时,备忘录模式非常有用。例如,文本编辑器中的撤销/重做功能。
  2. 不希望破坏对象的封装性

    • 如果直接访问对象的内部状态会导致封装性被破坏,备忘录模式可以通过封装状态保存和恢复逻辑来避免这种情况。
  3. 需要管理多个状态快照

    • 当需要保存多个状态快照,以便在不同时间点恢复对象时,备忘录模式可以很好地管理这些快照。例如,游戏中的存档功能。
  4. 状态恢复操作具有复杂性

    • 当状态恢复操作比较复杂,涉及到多个对象或多个状态时,备忘录模式可以将这些复杂的操作封装在 Originator 中,简化客户端代码。
示例代码

以下是一个简单的 C++ 示例,演示如何使用备忘录模式来实现文本编辑器的撤销功能。

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

// Memento: 存储 Originator 的状态
class Memento {
public:
    Memento(const std::string& state) : state(state) {}

    std::string getState() const {
        return state;
    }

private:
    std::string state;
};

// Originator: 需要保存和恢复状态的对象
class TextEditor {
public:
    void setText(const std::string& text) {
        this->text = text;
        std::cout << "Text set to: " << text << std::endl;
    }

    std::string getText() const {
        return text;
    }

    Memento save() const {
        return Memento(text);
    }

    void restore(const Memento& memento) {
        text = memento.getState();
        std::cout << "Restored to text: " << text << std::endl;
    }

private:
    std::string text;
};

// Caretaker: 管理 Memento 对象
class History {
public:
    void push(const Memento& memento) {
        history.push_back(memento);
    }

    Memento pop() {
        if (history.empty()) {
            throw std::runtime_error("No more mementos!");
        }
        Memento memento = history.back();
        history.pop_back();
        return memento;
    }

private:
    std::vector<Memento> history;
};

int main() {
    TextEditor editor;
    History history;

    // 设置文本并保存状态
    editor.setText("Hello, World!");
    history.push(editor.save());

    // 修改文本并保存状态
    editor.setText("Hello, Design Patterns!");
    history.push(editor.save());

    // 恢复到上一个状态
    editor.restore(history.pop());

    // 恢复到更早的状态
    editor.restore(history.pop());

    return 0;
}
解释
  • Memento 类:存储 TextEditor 的状态(文本内容)。
  • TextEditor 类 :需要保存和恢复状态的对象,提供了保存状态(save 方法)和恢复状态(restore 方法)的功能。
  • History 类:管理备忘录对象的集合,允许保存多个状态快照并按需恢复。
  • main 函数:演示如何使用备忘录模式实现文本编辑器的撤销功能。

适用性和优点

  1. 优点

    • 保持封装性:备忘录模式将状态保存逻辑封装在 Originator 内部,避免了直接暴露对象的内部状态。
    • 简化 Originator:状态保存和恢复逻辑被封装在 Memento 中,Originator 的代码更简洁。
    • 支持多级撤销:通过管理多个备忘录对象,可以实现多级撤销功能。
  2. 缺点

    • 内存消耗:如果需要保存大量状态快照,可能会占用大量内存。
    • 维护复杂性:当状态结构复杂时,备忘录对象的设计和维护可能变得复杂。

总结

备忘录模式是一种非常有用的设计模式,尤其是在需要保存和恢复对象状态的场景中。它通过封装状态保存和恢复逻辑,保持了对象的封装性,同时简化了状态管理的复杂性。通过结合 Caretaker 角色,备忘录模式还可以支持多级撤销等复杂功能。

备忘录模式(Memento Pattern)通常与其他设计模式协同使用,以增强其功能或简化实现。以下是一些常见的组合模式及其示例代码。

1. 备忘录模式与命令模式(Command Pattern)协同使用

协同场景

在需要实现撤销/重做功能时,备忘录模式可以与命令模式协同使用。命令模式用于封装操作,而备忘录模式用于保存对象的状态。

示例代码
cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

// Memento: 存储 Originator 的状态
class Memento {
public:
    Memento(const std::string& state) : state(state) {}
    std::string getState() const { return state; }

private:
    std::string state;
};

// Originator: 需要保存和恢复状态的对象
class TextEditor {
public:
    void setText(const std::string& text) {
        this->text = text;
        std::cout << "Text set to: " << text << std::endl;
    }

    std::string getText() const { return text; }

    Memento save() const { return Memento(text); }

    void restore(const Memento& memento) {
        text = memento.getState();
        std::cout << "Restored to text: " << text << std::endl;
    }

private:
    std::string text;
};

// Command: 命令接口
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};

// ConcreteCommand: 具体命令
class SetTextCommand : public Command {
public:
    SetTextCommand(TextEditor& editor, const std::string& newText)
        : editor(editor), newText(newText), backup(editor.save()) {}

    void execute() override {
        editor.setText(newText);
    }

    void undo() override {
        editor.restore(backup);
    }

private:
    TextEditor& editor;
    std::string newText;
    Memento backup;
};

// Invoker: 命令发起者
class CommandManager {
public:
    void execute(Command* command) {
        commandList.push_back(command);
        command->execute();
    }

    void undo() {
        if (commandList.empty()) {
            std::cout << "Nothing to undo!" << std::endl;
            return;
        }
        commandList.back()->undo();
        commandList.pop_back();
    }

private:
    std::vector<Command*> commandList;
};

int main() {
    TextEditor editor;
    CommandManager commandManager;

    // 设置初始文本
    editor.setText("Hello");

    // 执行命令:设置新文本
    commandManager.execute(new SetTextCommand(editor, "Hello, World!"));

    // 执行命令:再次设置新文本
    commandManager.execute(new SetTextCommand(editor, "Hello, Design Patterns!"));

    // 撤销命令
    commandManager.undo();
    commandManager.undo();

    return 0;
}
解释
  • Memento 类:用于保存 TextEditor 的状态。
  • TextEditor 类:需要保存和恢复状态的对象。
  • Command 接口:定义了命令的通用接口。
  • SetTextCommand 类:具体的命令实现,封装了文本设置操作,并使用备忘录模式保存前一个状态以便撤销。
  • CommandManager 类:管理命令的执行和撤销。

通过这种方式,备忘录模式与命令模式协同工作,实现了撤销功能。


2. 备忘录模式与观察者模式(Observer Pattern)协同使用

协同场景

在需要实现状态快照的实时通知时,备忘录模式可以与观察者模式协同使用。备忘录模式保存状态,而观察者模式通知其他对象状态的变化。

示例代码
cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

// Memento: 存储 Originator 的状态
class Memento {
public:
    Memento(const std::string& state) : state(state) {}
    std::string getState() const { return state; }

private:
    std::string state;
};

// Originator: 需要保存和恢复状态的对象
class TextEditor {
public:
    void setText(const std::string& text) {
        this->text = text;
        notifyObservers();
    }

    std::string getText() const { return text; }

    Memento save() const { return Memento(text); }

    void restore(const Memento& memento) {
        text = memento.getState();
        notifyObservers();
    }

    // 观察者模式相关方法
    void attach(class Observer* observer) {
        observers.push_back(observer);
    }

    void detach(class Observer* observer) {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notifyObservers() {
        for (auto observer : observers) {
            observer->update(save());
        }
    }

private:
    std::string text;
    std::vector<class Observer*> observers;
};

// Observer: 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const Memento& memento) = 0;
};

// ConcreteObserver: 具体观察者
class History : public Observer {
public:
    void update(const Memento& memento) override {
        history.push_back(memento);
        std::cout << "Snapshot saved: " << memento.getState() << std::endl;
    }

    Memento pop() {
        if (history.empty()) {
            throw std::runtime_error("No more mementos!");
        }
        Memento memento = history.back();
        history.pop_back();
        return memento;
    }

private:
    std::vector<Memento> history;
};

int main() {
    TextEditor editor;
    History history;

    // 注册观察者
    editor.attach(&history);

    // 设置文本
    editor.setText("Hello, World!");
    editor.setText("Hello, Design Patterns!");

    // 恢复状态
    editor.restore(history.pop());

    return 0;
}
解释
  • Memento 类:用于保存 TextEditor 的状态。
  • TextEditor 类:集成了备忘录模式和观察者模式。当状态更改时,通知所有观察者(如 History)。
  • Observer 接口:定义观察者的通用接口。
  • History 类:具体观察者,负责保存状态快照。

通过这种方式,备忘录模式与观察者模式协同工作,实现了状态变更的实时通知和快照保存。


3. 备忘录模式与迭代器模式(Iterator Pattern)协同使用

协同场景

在需要遍历历史状态时,备忘录模式可以与迭代器模式协同使用。迭代器模式用于遍历备忘录对象的集合。

示例代码
cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>

// Memento: 存储 Originator 的状态
class Memento {
public:
    Memento(const std::string& state) : state(state) {}
    std::string getState() const { return state; }

private:
    std::string state;
};

// Originator: 需要保存和恢复状态的对象
class TextEditor {
public:
    void setText(const std::string& text) {
        this->text = text;
    }

    std::string getText() const { return text; }

    Memento save() const { return Memento(text); }

    void restore(const Memento& memento) {
        text = memento.getState();
    }

private:
    std::string text;
};

// History: 管理 Memento 对象的集合
class History {
public:
    void push(const Memento& memento) {
        history.push_back(std::make_shared<Memento>(memento));
    }

    std::shared_ptr<Memento> pop() {
        if (history.empty()) {
            throw std::runtime_error("No more mementos!");
        }
        auto memento = history.back();
        history.pop_back();
        return memento;
    }

    class Iterator {
    public:
        Iterator(const std::vector<std::shared_ptr<Memento>>& history) : history(history), index(0) {}

        bool hasNext() const { return index < history.size(); }

        std::shared_ptr<Memento> next() {
            if (!hasNext()) {
                throw std::runtime_error("No more elements!");
            }
            return history[index++];
        }

    private:
        const std::vector<std::shared_ptr<Memento>>& history;
        size_t index;
    };

    Iterator createIterator() const { return Iterator(history); }

private:
    std::vector<std::shared_ptr<Memento>> history;
};

int main() {
    TextEditor editor;
    History history;

    // 保存状态
    editor.setText("Hello, World!");
    history.push(editor.save());

    editor.setText("Hello, Design Patterns!");
    history.push(editor.save());

    // 遍历历史状态
    History::Iterator iterator = history.createIterator();
    while (iterator.hasNext()) {
        std::cout << "Snapshot: " << iterator.next()->getState() << std::endl;
    }

    return 0;
}
解释
  • History 类:管理备忘录对象的集合,并提供迭代器功能。
  • Iterator 类:迭代器模式的核心,用于遍历备忘录对象的集合。

通过这种方式,备忘录模式与迭代器模式协同工作,实现了对历史状态的遍历。


总结

备忘录模式可以与其他设计模式(如命令模式、观察者模式和迭代器模式)协同使用,以增强系统的功能和灵活性。这些组合模式的应用场景包括撤销/重做、实时状态通知和历史状态遍历等。

相关推荐
不是仙人的闲人2 小时前
数据结构之栈和队列
数据结构·c++
重生之我是数学王子2 小时前
内核链表 例题 C语言实现
linux·c++
水宝的滚动歌词7 小时前
设计模式之建造者模式
java·设计模式·建造者模式
salsm8 小时前
使用 C++ 和函数式编程构建高效的 AI 模型
c++·人工智能
程序猿(雷霆之王)8 小时前
C++——继承
开发语言·c++
xianwu5438 小时前
mysql入门篇
开发语言·网络·c++·git
Upuping9 小时前
「全网最细 + 实战源码案例」设计模式——外观模式
java·后端·设计模式
qq_140303414410 小时前
数据结构9.3 - 文件基础(C++)
数据结构·c++
Lenyiin11 小时前
第431场周赛:最长乘积等价子数组、计算字符串的镜像分数、收集连续 K 个袋子可以获得的最多硬币数量、不重叠区间的最大得分
c++·算法·leetcode·周赛·lenyiin
渊渟岳11 小时前
掌握设计模式--抽象工厂模式
设计模式