📘 备忘录设计模式 vs 版本设计模式
引用:
世人痴情无数如戏一出唱韶华莫负叹此红尘阎浮为何偏作相思骨且把酒拂花遥相祝
🧩 一、核心概念与设计思想
🔮 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敏感型系统慎用(重建成本高) | 
📌 设计决策建议:
- 选择备忘录模式当:需严格封装状态 + 回滚需求简单 + 内存充足。
 - 选择版本模式当:需完整历史追溯 + 内存优化优先 + 可接受重建开销。