【C++】备忘录模式

目录

备忘录模式(Memento Pattern)是一种【行为型】设计模式,它允许在不破坏对象封装性的前提下,捕获并保存对象的内部状态,以便后续可以将对象恢复到先前的状态。这种模式通过将状态保存和恢复操作分离,实现了对象状态管理的职责分离。

一、模式核心概念与结构

备忘录模式包含三个核心角色:

  1. 原发器(Originator):创建备忘录并使用备忘录恢复内部状态的对象。
  2. 备忘录(Memento):存储原发器内部状态的对象,通常提供有限的访问权限。
  3. 管理者(Caretaker):负责保存备忘录,但不检查或修改备忘录的内容。

二、C++ 实现示例:文本编辑器撤销功能

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

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

// 前向声明
class Memento;

// 原发器:文本编辑器
class TextEditor {
private:
    std::string content;

public:
    void setContent(const std::string& text) {
        content = text;
        std::cout << "Current content: " << content << std::endl;
    }
    
    std::string getContent() const {
        return content;
    }
    
    // 创建备忘录
    std::shared_ptr<Memento> createMemento() const;
    
    // 从备忘录恢复
    void restoreFromMemento(std::shared_ptr<Memento> memento);
};

// 备忘录
class Memento {
private:
    std::string savedContent;

public:
    Memento(const std::string& content) : savedContent(content) {}
    
    // 仅供原发器访问的内部状态
    friend class TextEditor;
};

// 实现原发器的方法
std::shared_ptr<Memento> TextEditor::createMemento() const {
    return std::make_shared<Memento>(content);
}

void TextEditor::restoreFromMemento(std::shared_ptr<Memento> memento) {
    content = memento->savedContent;
    std::cout << "Restored content: " << content << std::endl;
}

// 管理者:历史记录
class History {
private:
    std::vector<std::shared_ptr<Memento>> mementos;

public:
    void saveState(std::shared_ptr<Memento> memento) {
        mementos.push_back(memento);
        std::cout << "State saved. History size: " << mementos.size() << std::endl;
    }
    
    std::shared_ptr<Memento> getPreviousState() {
        if (mementos.empty()) {
            std::cout << "No previous state" << std::endl;
            return nullptr;
        }
        
        auto last = mementos.back();
        mementos.pop_back();
        std::cout << "Restoring to previous state. History size: " << mementos.size() << std::endl;
        return last;
    }
};

// 客户端代码
int main() {
    // 创建文本编辑器和历史记录管理器
    auto editor = std::make_shared<TextEditor>();
    auto history = std::make_shared<History>();
    
    // 编辑文本并保存状态
    editor->setContent("Hello");
    history->saveState(editor->createMemento());
    
    editor->setContent("Hello World");
    history->saveState(editor->createMemento());
    
    editor->setContent("Hello World!");
    history->saveState(editor->createMemento());
    
    // 撤销操作
    editor->restoreFromMemento(history->getPreviousState());  // 恢复到 "Hello World"
    editor->restoreFromMemento(history->getPreviousState());  // 恢复到 "Hello"
    
    return 0;
}

三、备忘录模式的关键特性

  1. 封装状态
    • 备忘录对象封装了原发器的内部状态,保护其不被外部访问。
  2. 状态管理分离
    • 管理者负责保存备忘录,原发器负责创建和恢复状态,实现职责分离。
  3. 简化原发器
    • 原发器无需管理自己的历史记录,降低了复杂度。
  4. 支持撤销和恢复
    • 通过保存多个备忘录,可以实现多级撤销和恢复操作。

四、应用场景

  1. 撤销 / 重做功能
    • 文本编辑器、图形设计工具、游戏中的撤销操作。
    • 例如,Photoshop 中的历史记录面板。
  2. 事务管理
    • 数据库事务的回滚机制。
    • 例如,数据库在执行操作前保存状态,失败时回滚。
  3. 游戏存档
    • 保存游戏进度,支持读档功能。
  4. 对象版本控制
    • 保存对象的不同版本,支持版本回退。

五、备忘录模式与其他设计模式的关系

  1. 命令模式
    • 命令模式可以使用备忘录模式来实现命令的撤销功能。
    • 命令对象执行操作前创建备忘录,撤销时恢复状态。
  2. 迭代器模式
    • 可以结合迭代器模式遍历备忘录列表,实现历史记录的浏览。
  3. 原型模式
    • 备忘录可以通过克隆(原型模式)来创建对象的副本。

六、C++ 标准库中的备忘录模式应用

  1. 智能指针
    • std::shared_ptrstd::unique_ptr可以保存对象状态,支持资源管理。
  2. 序列化
    • 通过std::stringstream或 Boost.Serialization 可以将对象状态保存到流中。
  3. 异常处理
    • C++ 的异常处理机制可以看作是一种隐式的备忘录模式,异常抛出时保存调用栈状态。

七、优缺点分析

优点:

  • 封装性:保护对象内部状态不被外部访问。
  • 简化设计:将状态管理逻辑从原发器中分离。
  • 支持撤销:方便实现多级撤销和重做功能。
  • 符合开闭原则:新增备忘录类型无需修改现有代码。

缺点:

  • 资源消耗:保存大量备忘录可能占用过多内存。
  • 性能影响:频繁创建和恢复备忘录可能影响性能。
  • 维护成本:如果原发器状态复杂,备忘录的维护可能变得困难。

八、实战案例:游戏角色状态存档

以下是一个游戏角色状态存档的实现示例,支持角色状态的保存和恢复:

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

// 前向声明
class CharacterMemento;

// 原发器:游戏角色
class GameCharacter {
private:
    std::string name;
    int health;
    int level;
    std::string weapon;

public:
    GameCharacter(const std::string& n, int h, int l, const std::string& w)
        : name(n), health(h), level(l), weapon(w) {}
    
    void takeDamage(int damage) {
        health -= damage;
        if (health < 0) health = 0;
        std::cout << name << " takes " << damage << " damage. Health: " << health << std::endl;
    }
    
    void levelUp() {
        level++;
        std::cout << name << " levels up to " << level << std::endl;
    }
    
    void changeWeapon(const std::string& newWeapon) {
        weapon = newWeapon;
        std::cout << name << " changes weapon to " << weapon << std::endl;
    }
    
    void displayStatus() const {
        std::cout << "=== " << name << " Status ===" << std::endl;
        std::cout << "Health: " << health << std::endl;
        std::cout << "Level: " << level << std::endl;
        std::cout << "Weapon: " << weapon << std::endl;
        std::cout << "====================" << std::endl;
    }
    
    // 创建备忘录
    std::shared_ptr<CharacterMemento> saveState() const;
    
    // 从备忘录恢复
    void restoreState(std::shared_ptr<CharacterMemento> memento);
};

// 备忘录
class CharacterMemento {
private:
    std::string savedName;
    int savedHealth;
    int savedLevel;
    std::string savedWeapon;

public:
    CharacterMemento(const std::string& n, int h, int l, const std::string& w)
        : savedName(n), savedHealth(h), savedLevel(l), savedWeapon(w) {}
    
    // 仅供原发器访问的内部状态
    friend class GameCharacter;
};

// 实现原发器的方法
std::shared_ptr<CharacterMemento> GameCharacter::saveState() const {
    return std::make_shared<CharacterMemento>(name, health, level, weapon);
}

void GameCharacter::restoreState(std::shared_ptr<CharacterMemento> memento) {
    name = memento->savedName;
    health = memento->savedHealth;
    level = memento->savedLevel;
    weapon = memento->savedWeapon;
    std::cout << name << " restored to previous state" << std::endl;
}

// 管理者:游戏存档系统
class GameSaveSystem {
private:
    std::vector<std::shared_ptr<CharacterMemento>> saves;

public:
    void saveGame(std::shared_ptr<CharacterMemento> memento) {
        saves.push_back(memento);
        std::cout << "Game saved. Save count: " << saves.size() << std::endl;
    }
    
    std::shared_ptr<CharacterMemento> loadGame(int index) {
        if (index < 0 || index >= saves.size()) {
            std::cout << "Invalid save index" << std::endl;
            return nullptr;
        }
        
        std::cout << "Loading save " << index << std::endl;
        return saves[index];
    }
    
    void displaySaves() const {
        std::cout << "=== Save Files ===" << std::endl;
        for (size_t i = 0; i < saves.size(); i++) {
            std::cout << i << ". Save File" << std::endl;
        }
        std::cout << "==================" << std::endl;
    }
};

// 客户端代码
int main() {
    // 创建游戏角色和存档系统
    auto player = std::make_shared<GameCharacter>("Hero", 100, 1, "Sword");
    auto saveSystem = std::make_shared<GameSaveSystem>();
    
    // 显示初始状态
    player->displayStatus();
    
    // 保存初始状态
    saveSystem->saveGame(player->saveState());
    
    // 进行一些操作
    player->takeDamage(20);
    player->levelUp();
    player->changeWeapon("Greatsword");
    
    // 显示当前状态
    player->displayStatus();
    
    // 保存当前状态
    saveSystem->saveGame(player->saveState());
    
    // 再进行一些操作
    player->takeDamage(50);
    player->displayStatus();
    
    // 恢复到第一个存档
    player->restoreState(saveSystem->loadGame(0));
    player->displayStatus();
    
    // 恢复到第二个存档
    player->restoreState(saveSystem->loadGame(1));
    player->displayStatus();
    
    return 0;
}

九、实现注意事项

  1. 访问控制
    • 备忘录的状态应仅由原发器访问,可通过友元类或接口隔离实现。
  2. 备忘录大小
    • 避免保存不必要的状态,减少内存消耗。对于大型对象,可考虑增量保存。
  3. 生命周期管理
    • 确保管理者正确管理备忘录的生命周期,避免内存泄漏。
  4. 序列化支持
    • 对于需要持久化的场景,备忘录应支持序列化和反序列化。

备忘录模式是 C++ 中实现对象状态管理的重要工具,通过封装状态保存和恢复逻辑,使系统在不破坏封装性的前提下支持撤销、重做和历史记录等功能,提高了代码的可维护性和用户体验。


如果这篇文章对你有所帮助,渴望获得你的一个点赞!

相关推荐
GiraKoo10 分钟前
【GiraKoo】C++11的新特性
c++·后端
不午睡的探索者13 分钟前
告别性能瓶颈!Python 量化工程师,进击 C++ 高性能量化交易的“必修课”!
c++·github
OpenC++14 分钟前
【C++】观察者模式
c++·观察者模式·设计模式
老歌老听老掉牙23 分钟前
粒子群优化算法实现与多维函数优化应用
c++·pso·粒子群算法
myloveasuka1 小时前
信号操作集函数
linux·运维·服务器·c语言·c++·vscode
山野万里__1 小时前
C++与Java内存共享技术:跨平台与跨语言实现指南
android·java·c++·笔记
一块plus2 小时前
2025 年值得一玩的最佳 Web3 游戏
算法·设计模式·程序员
Mr_Xuhhh2 小时前
网络基础(1)
c语言·开发语言·网络·c++·qt·算法
缘来是庄2 小时前
设计模式之代理模式
java·设计模式·代理模式
醇醛酸醚酮酯3 小时前
std::promise和std::future的使用示例——单线程多链接、多线程单链接
网络·c++·算法