首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ 備忘錄模式完整實作與應用指南

     編輯:C++ 入門

前言

備忘錄模式是行為型設計模式之一,用於在不破壞封裝性的前提下,捕獲物件內部狀態並外部化儲存,以便日後恢復。典型應用包括:編輯器的「復原/重做」、遊戲存檔讀檔、資料庫交易回滾、IDE 程式碼快照等。本文提供標準 C++ 實作範例,並解析關鍵設計細節。

一、模式結構與角色

  • Originator(發起人): 需要被保存狀態的物件,負責建立備忘錄、從備忘錄恢復狀態。
  • Memento(備忘錄): 儲存 Originator 內部狀態,僅允許 Originator 存取(透過 friend 或公開介面)。
  • Caretaker(管理者): 負責保管備忘錄,不檢查內容,僅負責儲存/提供。

二、核心程式碼實作

1. 狀態型別與備忘錄類別

#include <iostream>
#include <string>
#include <memory>

using State = std::string;

class Memento {
    friend class Originator;          // 僅 Originator 可存取內部狀態
    State state_;
    explicit Memento(const State& s) : state_(s) {}
public:
    ~Memento() = default;
    // 禁止外部複製/移動,確保封裝
    Memento(const Memento&) = delete;
    Memento& operator=(const Memento&) = delete;
    State getState() const { return state_; }
};

2. 發起人類別

class Originator {
    State state_;
public:
    Originator() = default;
    explicit Originator(const State& s) : state_(s) {}

    // 建立備忘錄(轉移所有權給呼叫者)
    std::unique_ptr<Memento> createMemento() {
        return std::make_unique<Memento>(state_);
    }

    // 從備忘錄恢復
    void restoreFromMemento(const Memento* m) {
        if (m) state_ = m->getState();
    }

    // 業務介面
    void setState(const State& s) { state_ = s; }
    State getState() const { return state_; }
    void printState() const { std::cout << state_ << ".." << std::endl; }
};

3. 管理者(簡易示範)

class Caretaker {
    std::vector<std::unique_ptr<Memento>> history_;
    size_t current_ = 0;  // 指向當前狀態
public:
    void save(Originator& o) {
        // 丟棄 current 之後的未來(重做堆疊)
        history_.resize(current_);
        history_.push_back(o.createMemento());
        ++current_;
    }
    void undo(Originator& o) {
        if (current_ == 0) return;
        --current_;
        o.restoreFromMemento(history_[current_].get());
    }
    void redo(Originator& o) {
        if (current_ >= history_.size()) return;
        o.restoreFromMemento(history_[current_].get());
        ++current_;
    }
};

4. 客戶端測試

int main() {
    Originator originator("old");
    Caretaker caretaker;

    originator.printState();           // old..
    caretaker.save(originator);

    originator.setState("new");
    originator.printState();           // new..
    caretaker.save(originator);

    caretaker.undo(originator);
    originator.printState();           // old..

    caretaker.redo(originator);
    originator.printState();           // new..
    return 0;
}

三、關鍵設計細節解析

1. 封裝性保護

Memento 將建構函式設為 private,並宣告 Originator 為 friend,確保只有發起人能建立/讀取備忘錄。外部(含 Caretaker)僅能持有不透明指標,無法竄改內部狀態。

2. 所有權語意

使用 std::unique_ptr<Memento> 明確所有權轉移:createMemento 回傳唯一擁有權給 Caretaker,避免記憶體洩漏與雙重釋放。

3. 狀態型別泛化

範例使用 using State = std::string,實務上可為任意可複製/可序列化的型別(如 struct GameState { int hp, mp; std::vector<Item> items; };)。

4. 多層級復原/重做

Caretaker 以向量 + 索引實作線性歷史,支援無限級 Undo/Redo。若記憶體敏感,可改用固定大小環狀緩衝區。

四、進階變體與擴展

  • 增量備忘錄: 僅儲存變更差異,適合大型物件(如文字編輯器逐字元儲存)。
  • 原型模式結合: 若 Originator 本身支援 clone(),可直接複製整個物件作為備忘錄,簡化實作但犧牲封裝性。
  • 序列化持久化: 將 Memento 序列化為 JSON/二進位寫入磁碟,實現遊戲存檔、專案快照。

五、常見誤區與最佳實踐

誤區正確做法
讓 Caretaker 直接存取 Memento 內部資料Memento 應為不透明型別,Caretaker 僅負責保管指標
使用裸指標手動管理 Memento 生命週期採用智慧指標(unique_ptr/shared_ptr)自動管理
備忘錄包含大量重複資料導致記憶體暴增採用增量儲存、壓縮、或限制歷史深度
多執行緒環境下未同步歷史堆疊加鎖或使用無鎖資料結構保護 Caretaker

結語

備忘錄模式以優雅的封裝方式實現狀態捕獲與恢復,是實作 Undo/Redo、存檔讀檔、交易回滾的標準解法。掌握「發起人-備忘錄-管理者」三角色協作、善用現代 C++ 智慧指標與移動語意,即能編寫安全、高效、可擴展的狀態管理系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved