备忘录设计模式 vs 版本设计模式

📘 备忘录设计模式 vs 版本设计模式

引用

  1. 世人痴情无数 如戏一出 唱韶华莫负
  2. 叹此红尘阎浮 为何偏作相思骨 且把酒 拂花遥相祝

🧩 一、核心概念与设计思想

🔮 1. 备忘录模式(Memento Pattern)

设计思想状态封装 + 外部存储

  • 核心目标:在不破坏对象封装性的前提下,捕获并存储其内部状态,支持状态回滚。
  • 三元素模型
    creates stores Originator +SetState() +CreateMemento() +RestoreMemento(m: Memento) Memento -state: string +GetState() Caretaker -memento: Memento +Save(m: Memento) +Retrieve()
  • 思想重点
    • 封装性保护:Memento仅对Originator暴露完整状态,Caretaker无法直接修改状态。
    • 单一快照:每次保存独立状态快照,无历史关联性。
🔁 2. 版本模式(Versioning Pattern)

设计思想历史链 + 增量管理

  • 核心目标:维护对象所有历史状态记录,支持任意历史版本回溯和比较。
  • 链式存储模型
    contains * Document -content: string -versionHistory: Version* +CommitVersion(version: Version) +GetVersion(vId: int) Version -id: int -timestamp: time -delta: string // 变化量 +ApplyDelta()
  • 思想重点
    • 版本关联:版本间通过增量(delta)链接,形成时间线。
    • 高效存储:仅存储变化量而非全量数据,降低内存占用。

⚙️ 二、C++实现对比

📂 1. 备忘录模式实现
cpp 复制代码
#include <iostream>  
#include <string>  
#include <vector>  

class Memento {  
private:  
    std::string state;  
    friend class Originator; // 仅允许Originator访问  
    Memento(const std::string& s) : state(s) {}  
    std::string GetState() const { return state; }  
};  

class Originator {  
private:  
    std::string state;  
public:  
    void SetState(const std::string& s) { state = s; }  
    Memento* CreateMemento() { return new Memento(state); }  
    void RestoreMemento(const Memento* m) { state = m->GetState(); }  
    void Print() { std::cout << "Current State: " << state << std::endl; }  
};  

class Caretaker {  
private:  
    std::vector<Memento*> history;  
public:  
    void Save(Memento* m) { history.push_back(m); }  
    Memento* Retrieve(int index) { return history[index]; }  
};  

// 使用示例  
int main() {  
    Originator editor;  
    Caretaker history;  

    editor.SetState("Version1");  
    history.Save(editor.CreateMemento());  
    editor.Print(); // Output: Version1  

    editor.SetState("Version2");  
    history.Save(editor.CreateMemento());  

    editor.RestoreMemento(history.Retrieve(0));  
    editor.Print(); // Output: Version1 (回滚成功)  
}  

关键点

  • Memento构造函数私有,仅Originator可创建。
  • Caretaker存储历史快照,但无法修改内容。
🔗 2. 版本模式实现
cpp 复制代码
#include <iostream>  
#include <string>  
#include <vector>  
#include <ctime>  

class Version {  
public:  
    int id;  
    time_t timestamp;  
    std::string delta; // 存储变化量  
    Version(int vId, const std::string& d) : id(vId), delta(d) {  
        timestamp = time(nullptr);  
    }  
};  

class Document {  
private:  
    std::string content;  
    std::vector<Version*> history;  
public:  
    void AppendContent(const std::string& text) {  
        Version* v = new Version(history.size()+1, text);  
        history.push_back(v);  
        content += text; // 应用新内容  
    }  
    void RevertToVersion(int vId) {  
        content = ""; // 重建历史  
        for (int i=0; i<=vId; ++i) {  
            content += history[i]->delta;  
        }  
    }  
    void PrintHistory() {  
        for (auto& v : history) {  
            std::cout << "v" << v->id << " at " << v->timestamp << std::endl;  
        }  
    }  
};  

// 使用示例  
int main() {  
    Document doc;  
    doc.AppendContent("Hello");  
    doc.AppendContent(" World");  
    doc.PrintHistory();  
    doc.RevertToVersion(0); // 回退到"Hello"  
}  

关键点

  • 使用delta存储增量变化,非全量数据。
  • 版本号(id)和时间戳用于追踪历史。

🔍 三、核心差异对比

维度 备忘录模式 版本模式
状态存储方式 全量快照 增量(delta)链式存储
历史关联性 无版本关联 显式版本链(时间线)
封装性保护 严格(仅Originator可操作Memento) 弱(Document直接管理Version)
内存效率 低(全量存储) 高(仅存变化量)
回溯灵活性 仅支持离散快照回滚 支持任意版本跳跃

🎯 四、适用场景与优劣分析

📌 1. 备忘录模式

✅ 适用场景

  • 需要回滚操作的场景(如编辑器撤销、游戏存档)。
  • 需严格保护对象内部状态的系统(如金融事务快照)。
  • 状态变化频率较低的场景。

⛔ 缺点

  • 内存占用高:每个快照存储全量数据。
  • 历史关联弱:无法追踪状态变化路径。
📊 2. 版本模式

✅ 适用场景

  • 需详细历史追踪的系统(如Git版本控制、文档协同编辑)。
  • 高频状态变化的场景(如实时协作白板)。
  • 需要版本比较/分支管理的应用。

⛔ 缺点

  • 实现复杂度高:需设计增量存储和重建逻辑。
  • 回滚性能问题:版本链越长,重建越耗时。

🔬 五、设计思想深度剖析

🧠 1. 状态管理哲学
  • 备忘录模式状态即孤岛

    • 每个快照独立存在,无上下文依赖。
    • 符合"隔离性"设计原则(如事务的ACID特性)。
  • 版本模式状态即时间流

    • 状态是历史累积的结果。
    • 强调因果关联性,符合事件溯源(Event Sourcing)思想。
💡 2. 性能与存储权衡
模式 存储成本 恢复成本
备忘录模式 O(n) * 全量大小 O(1)(直接替换)
版本模式 O(n) * 变化量大小 O(k)(k为目标版本)

🏁 六、总结

维度 备忘录模式 版本模式
核心思想 状态隔离快照 历史版本链
最佳应用 撤销操作、事务回滚 版本控制、协同编辑
扩展性 低(新增状态需全量保存) 高(增量存储易扩展)
系统影响 内存敏感型系统慎用 CPU敏感型系统慎用(重建成本高)

📌 设计决策建议

  • 选择备忘录模式当:需严格封装状态 + 回滚需求简单 + 内存充足。
  • 选择版本模式当:需完整历史追溯 + 内存优化优先 + 可接受重建开销。
相关推荐
千夕见夕1 分钟前
C 语言指针深度解析:从数组指针到指针函数的实战指南
c语言·c++·算法
Yuroo zhou1 小时前
无人机在复杂气流中,IMU 如何精准捕捉姿态变化以维持稳定?
单片机·嵌入式硬件·算法·机器人·无人机
Dream it possible!1 小时前
LeetCode 面试经典 150_数组/字符串_买卖股票的最佳时机(7_121_C++_简单)(贪心)
c++·leetcode·面试·贪心算法
xueyongfu1 小时前
PTX指令集基础以及warp级矩阵乘累加指令介绍
人工智能·线性代数·算法·矩阵
流星白龙1 小时前
【C++算法】89.多源BFS_01 矩阵
c++·算法·宽度优先
lly2024062 小时前
HTML 表单
开发语言
爱代码的小黄人3 小时前
利用劳斯判据分析右半平面极点数量的方法研究
算法·机器学习·平面
深海潜水员5 小时前
【Python】 切割图集的小脚本
开发语言·python
Yolo566Q5 小时前
R语言与作物模型(以DSSAT模型为例)融合应用高级实战技术
开发语言·经验分享·r语言
Felven6 小时前
C. Challenging Cliffs
c语言·开发语言