17370845950

c++如何实现备忘录模式 c++设计模式之Memento【实例】
备忘录模式通过Originator、Memento、Caretaker三类角色实现对象状态的保存与恢复:Originator创建/恢复状态并声明Memento为友元;Memento封装只读快照;Caretaker以黑盒方式管理备忘录集合。

备忘录模式(Memento Pattern)用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便之后可以恢复到该状态。C++ 中实现该模式需明确三类角色:发起人(Originator)、备忘录(Memento)、管理者(Caretaker)。

Originator:负责创建和恢复状态

发起人持有需要保存/恢复的核心数据,提供 save() 创建备忘录、restore(Memento) 恢复状态的方法。关键点是备忘录类应为 Originator 的友元,或通过公有接口隔离内部细节——推荐用友元方式保证封装性,同时允许安全访问私有成员。

示例:

class Originator {
private:
    std::string state;
public:
    Originator(const std::string& s) : state(s) {}
void setState(const std::string& s) { state = s; }
std::string getState() const { return state; }

// 创建备忘录:返回一个包含当前状态的 Memento 对象
Memento save() const { return Memento(state); }

// 从备忘录恢复状态
void restore(const Memento& m) { state = m.getState(); }

};

Memento:只读快照,封装内部状态

备忘录对象仅用于存储快照,对外不提供修改接口。它通常将状态设为私有,并只允许 Originator 访问(通过 friend 声明)。避免暴露 setter 或直接访问字段,防止外部篡改历史状态。

示例:

class Memento {
private:
    std::string state;
public:
    explicit Memento(const std::string& s) : state(s) {}
// 只提供只读访问
std::string getState() const { return state; }

// 关键:声明 Originator 为友元,允许其读写私有 state
friend class Originator;

};

Caretaker:管理多个备忘录,不窥探内容

管理者负责保存、索引、按需提供备忘录,但它不能访问或修改备忘录内部数据。它只把 Memento 当作黑盒处理。常见做法是用 vector 或 stack 存储备忘录,支持撤销(undo)或重做(redo)逻辑。

示例:

class Caretaker {
private:
    std::vector history;
public:
    void addMemento(const Memento& m) {
        history.push_back(m);
    }
Memento getMemento(size_t index) const {
    if (index < history.size()) {
        return history[index];
    }
    throw std::out_of_range("Invalid memento index");
}

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

};

完整使用示例

组合三者,模拟编辑器文本状态的保存与回滚:

int main() {
    Originator editor("Initial content");
    Caretaker history;
std::cout << "Current: " << editor.getState() << "\n";

history.addMemento(editor.save()); // 保存初始状态

editor.setState("After first edit");
std::cout << "Edited: " << editor.getState() << "\n";

history.addMemento(editor.save()); // 保存编辑后状态

editor.setState("After second edit");
std::cout << "Edited again: " << editor.getState() << "\n";

// 撤销:恢复到上一个备忘录
editor.restore(history.getMemento(history.size() - 2));
std::cout << "Restored: " << editor.getState() << "\n"; // 输出 "After first edit"

}

注意:若需支持多次撤销,可配合栈结构(std::stack)实现 LIFO 管理;如需带时间戳或命名,可在 Memento 中扩展字段,但保持对外只读;多线程环境下需考虑 history 的线程安全,可加锁或使用线程局部存储。