行为型模式:⑥备忘录模式(Memento Pattern)

行为型模式:⑥备忘录模式(Memento Pattern)

核心思想

在不破坏对象封装性的前提下,捕获对象的内部状态并保存到外部(备忘录),后续可通过备忘录将对象恢复到之前的状态。核心是分离 “对象状态的保存 / 恢复” 与 “对象自身的业务逻辑”,通过三个角色实现解耦:
发起人(Originator):拥有内部状态,负责创建备忘录(保存状态)和从备忘录恢复状态;
备忘录(Memento):存储发起人的内部状态,仅允许发起人访问(保证封装性);
管理者(Caretaker):负责管理备忘录(存储、获取),不操作备忘录的内部状态(不感知状态细节)。

核心本质

状态快照 + 封装隔离,既实现状态的可回溯(如撤销、回滚),又不暴露对象的内部结构。

典型场景

文本编辑器的撤销 / 重做功能(保存每次编辑后的文本状态);
游戏存档 / 读档(保存角色血量、装备、关卡等状态);
配置中心的配置回滚(保存历史配置版本);
数据库事务的回滚(保存事务执行前的数据状态)。

C 语言实现(结构体 + 不透明指针 + 封装状态)

C 语言无类和访问控制,通过 “不透明指针(Opaque Pointer)” 实现备忘录的封装(隐藏内部状态),用结构体模拟三个核心角色,函数指针定义操作接口。

场景示例

简单文本编辑器:
发起人(TextEditor):包含文本内容、光标位置,支持编辑、保存状态、恢复状态;
备忘录(Memento):通过不透明指针隐藏内部状态,仅允许发起人访问;
管理者(Caretaker):用数组存储多个备忘录(支持多步撤销),提供添加 / 获取备忘录接口。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 最大文本长度
#define MAX_TEXT_LEN 256
// 最大撤销步数(备忘录存储上限)
#define MAX_UNDO_STEPS 10

// -------------------------- 1. 备忘录(Memento):不透明指针封装状态 --------------------------
// 前置声明:外部仅能通过指针操作,无法访问内部结构(保证封装)
typedef struct Memento Memento;

// 备忘录操作接口(仅发起人可调用,外部通过发起人间接使用)
Memento* memento_create(const char* text, int cursor_pos);
void memento_get_state(const Memento* self, char* text, int* cursor_pos);
void memento_destroy(Memento* self);

// -------------------------- 2. 发起人(Originator):文本编辑器 --------------------------
typedef struct TextEditor {
    char text[MAX_TEXT_LEN];   // 内部状态1:文本内容
    int cursor_pos;            // 内部状态2:光标位置
    // 操作接口
    void (*edit)(struct TextEditor* self, const char* append_str); // 编辑文本
    Memento* (*save)(struct TextEditor* self);                     // 保存状态到备忘录
    void (*restore)(struct TextEditor* self, const Memento* memento); // 从备忘录恢复
    void (*destroy)(struct TextEditor* self);
} TextEditor;

// 发起人:编辑文本(追加字符串,更新光标位置)
void text_editor_edit(TextEditor* self, const char* append_str) {
    if (strlen(self->text) + strlen(append_str) >= MAX_TEXT_LEN) {
        printf("文本长度超出限制,无法继续编辑!\n");
        return;
    }
    strcat(self->text, append_str);
    self->cursor_pos = strlen(self->text); // 光标移到末尾
    printf("编辑后:文本=%s,光标位置=%d\n", self->text, self->cursor_pos);
}

// 发起人:保存状态到备忘录(创建备忘录并写入当前状态)
Memento* text_editor_save(TextEditor* self) {
    Memento* memento = memento_create(self->text, self->cursor_pos);
    printf("已保存状态:文本=%s,光标位置=%d\n", self->text, self->cursor_pos);
    return memento;
}

// 发起人:从备忘录恢复状态(读取备忘录的状态并更新自身)
void text_editor_restore(TextEditor* self, const Memento* memento) {
    memento_get_state(memento, self->text, &self->cursor_pos);
    printf("已恢复状态:文本=%s,光标位置=%d\n", self->text, self->cursor_pos);
}

// 发起人:销毁
void text_editor_destroy(TextEditor* self) {
    free(self);
}

// 创建发起人(文本编辑器)
TextEditor* create_text_editor() {
    TextEditor* editor = (TextEditor*)malloc(sizeof(TextEditor));
    memset(editor->text, 0, MAX_TEXT_LEN);
    editor->cursor_pos = 0;
    editor->edit = text_editor_edit;
    editor->save = text_editor_save;
    editor->restore = text_editor_restore;
    editor->destroy = text_editor_destroy;
    return editor;
}

// -------------------------- 3. 管理者(Caretaker):管理备忘录(撤销栈) --------------------------
typedef struct Caretaker {
    Memento* mementos[MAX_UNDO_STEPS]; // 存储备忘录的栈(先进后出,支持撤销)
    int top;                           // 栈顶索引(-1表示空)
    // 操作接口
    void (*push_memento)(struct Caretaker* self, Memento* memento); // 添加备忘录
    Memento* (*pop_memento)(struct Caretaker* self);                // 获取最新备忘录(撤销)
    void (*destroy)(struct Caretaker* self);                        // 销毁管理者(释放所有备忘录)
} Caretaker;

// 管理者:添加备忘录到栈
void caretaker_push_memento(Caretaker* self, Memento* memento) {
    if (self->top >= MAX_UNDO_STEPS - 1) {
        printf("撤销步数已达上限,丢弃最早的状态!\n");
        // 丢弃栈底备忘录(先进先出)
        memento_destroy(self->mementos[0]);
        // 所有元素前移
        for (int i = 0; i < self->top; i++) {
            self->mementos[i] = self->mementos[i + 1];
        }
        self->top--;
    }
    self->mementos[++self->top] = memento;
}

// 管理者:弹出最新的备忘录(撤销)
Memento* caretaker_pop_memento(Caretaker* self) {
    if (self->top < 0) {
        printf("无历史状态可撤销!\n");
        return NULL;
    }
    return self->mementos[self->top--];
}

// 管理者:销毁(释放所有备忘录)
void caretaker_destroy(Caretaker* self) {
    for (int i = 0; i <= self->top; i++) {
        memento_destroy(self->mementos[i]);
    }
    free(self);
}

// 创建管理者
Caretaker* create_caretaker() {
    Caretaker* caretaker = (Caretaker*)malloc(sizeof(Caretaker));
    caretaker->top = -1;
    caretaker->push_memento = caretaker_push_memento;
    caretaker->pop_memento = caretaker_pop_memento;
    caretaker->destroy = caretaker_destroy;
    memset(caretaker->mementos, 0, sizeof(caretaker->mementos));
    return caretaker;
}

// -------------------------- 备忘录(Memento)具体实现(不透明指针的核心:仅在.c文件中定义) --------------------------
struct Memento {
    char text[MAX_TEXT_LEN];   // 存储发起人的文本状态
    int cursor_pos;            // 存储发起人的光标位置
};

// 创建备忘录(保存状态)
Memento* memento_create(const char* text, int cursor_pos) {
    Memento* memento = (Memento*)malloc(sizeof(Memento));
    strncpy(memento->text, text, MAX_TEXT_LEN);
    memento->cursor_pos = cursor_pos;
    return memento;
}

// 获取备忘录中的状态(仅发起人可调用,外部无法直接访问)
void memento_get_state(const Memento* self, char* text, int* cursor_pos) {
    strncpy(text, self->text, MAX_TEXT_LEN);
    *cursor_pos = self->cursor_pos;
}

// 销毁备忘录
void memento_destroy(Memento* self) {
    free(self);
}

// -------------------------- 测试代码(客户端) --------------------------
int main() {
    // 1. 创建发起人(文本编辑器)和管理者(撤销栈)
    TextEditor* editor = create_text_editor();
    Caretaker* caretaker = create_caretaker();

    // 2. 编辑文本并保存状态
    printf("=== 第一次编辑 ===\n");
    editor->edit(editor, "Hello ");
    caretaker->push_memento(caretaker, editor->save(editor)); // 保存状态1

    printf("\n=== 第二次编辑 ===\n");
    editor->edit(editor, "World! ");
    caretaker->push_memento(caretaker, editor->save(editor)); // 保存状态2

    printf("\n=== 第三次编辑 ===\n");
    editor->edit(editor, "This is Memento Pattern.");
    caretaker->push_memento(caretaker, editor->save(editor)); // 保存状态3

    // 3. 撤销操作(恢复到状态2)
    printf("\n=== 第一次撤销 ===\n");
    Memento* m1 = caretaker->pop_memento(caretaker);
    if (m1 != NULL) {
        editor->restore(editor, m1);
        memento_destroy(m1); // 恢复后释放备忘录(管理者已弹出,不再管理)
    }

    // 4. 再次撤销(恢复到状态1)
    printf("\n=== 第二次撤销 ===\n");
    Memento* m2 = caretaker->pop_memento(caretaker);
    if (m2 != NULL) {
        editor->restore(editor, m2);
        memento_destroy(m2);
    }

    // 5. 第三次撤销(恢复到初始状态)
    printf("\n=== 第三次撤销 ===\n");
    Memento* m3 = caretaker->pop_memento(caretaker);
    if (m3 != NULL) {
        editor->restore(editor, m3);
        memento_destroy(m3);
    }

    // 6. 第四次撤销(无状态)
    printf("\n=== 第四次撤销 ===\n");
    caretaker->pop_memento(caretaker);

    // 7. 销毁资源
    editor->destroy(editor);
    caretaker->destroy(caretaker);

    return 0;
}

实现关键要点

1.备忘录封装(不透明指针):Memento结构体仅在.c 文件中定义,.h 文件仅声明指针和操作函数,外部无法直接访问其内部状态(text、cursor_pos),严格遵循 “仅发起人可访问” 的封装原则。
2.角色职责清晰:
发起人:仅负责自身业务(编辑)和状态的保存 / 恢复,不管理备忘录;
备忘录:仅存储状态,不包含业务逻辑;
管理者:仅存储和提供备忘录,不感知状态细节,避免破坏封装。
3.状态完整性:备忘录需完整存储发起人的所有关键状态(如文本 + 光标位置),否则恢复后状态不完整。
4.内存管理:管理者销毁时需遍历释放所有备忘录,发起人恢复后需释放弹出的备忘录,避免内存泄漏。
5.撤销栈设计:管理者用数组实现栈结构(先进后出),支持多步撤销,超过上限时丢弃最早状态,符合实际应用场景(如编辑器的撤销历史上限)。
6.扩展性:新增发起人状态(如选中区域)时,仅需修改Memento结构体和发起人的save/restore方法,管理者无需修改,符合开闭原则。

C++ 实现(类 + 友元 + 智能指针 + 封装)

C++ 通过 “类继承 + 友元 + 智能指针” 实现备忘录模式,友元机制保证发起人可访问备忘录的私有状态,智能指针自动管理内存,STL 容器简化备忘录存储。

场景示例

同 C 语言:文本编辑器,三个角色用类实现,备忘录通过友元与发起人绑定,保证封装性。

#include <iostream>
#include <string>
#include <vector>
#include <memory> // 智能指针

using namespace std;

// 前置声明:发起人(供备忘录使用)
class TextEditor;

// -------------------------- 1. 备忘录(Memento):封装状态,仅允许发起人访问 --------------------------
class Memento {
private:
    // 备忘录私有状态(仅发起人可访问)
    string text_;     // 存储文本
    int cursor_pos_;  // 存储光标位置

    // 友元声明:仅TextEditor可访问私有成员(保证封装)
    friend class TextEditor;

    // 私有构造函数:仅发起人可创建备忘录
    Memento(string text, int cursor_pos)
        : text_(move(text)), cursor_pos_(cursor_pos) {}

public:
    // 禁止外部修改备忘录(无setter方法),保证状态不可变
    ~Memento() = default;
};

// -------------------------- 2. 发起人(Originator):文本编辑器 --------------------------
class TextEditor {
public:
    TextEditor() : cursor_pos_(0) {}

    // 编辑文本
    void edit(const string& append_str) {
        if (text_.size() + append_str.size() > MAX_TEXT_LEN) {
            cout << "文本长度超出限制,无法继续编辑!" << endl;
            return;
        }
        text_ += append_str;
        cursor_pos_ = text_.size();
        cout << "编辑后:文本=" << text_ << ",光标位置=" << cursor_pos_ << endl;
    }

    // 保存状态到备忘录(创建备忘录,直接访问其私有构造函数)
    shared_ptr<Memento> save() {
        cout << "已保存状态:文本=" << text_ << ",光标位置=" << cursor_pos_ << endl;
        return make_shared<Memento>(text_, cursor_pos_);
    }

    // 从备忘录恢复状态(直接访问备忘录的私有成员)
    void restore(const shared_ptr<Memento>& memento) {
        text_ = memento->text_;
        cursor_pos_ = memento->cursor_pos_;
        cout << "已恢复状态:文本=" << text_ << ",光标位置=" << cursor_pos_ << endl;
    }

private:
    static const int MAX_TEXT_LEN = 256; // 最大文本长度
    string text_;                        // 文本内容
    int cursor_pos_;                     // 光标位置
};

// -------------------------- 3. 管理者(Caretaker):管理备忘录(撤销栈) --------------------------
class Caretaker {
public:
    static const int MAX_UNDO_STEPS = 10; // 最大撤销步数

    // 添加备忘录到栈
    void pushMemento(shared_ptr<Memento> memento) {
        if (mementos_.size() >= MAX_UNDO_STEPS) {
            cout << "撤销步数已达上限,丢弃最早的状态!" << endl;
            mementos_.erase(mementos_.begin()); // 丢弃最早状态(vector模拟栈,队首为最早)
        }
        mementos_.push_back(memento);
    }

    // 弹出最新的备忘录(撤销)
    shared_ptr<Memento> popMemento() {
        if (mementos_.empty()) {
            cout << "无历史状态可撤销!" << endl;
            return nullptr;
        }
        shared_ptr<Memento> last = mementos_.back();
        mementos_.pop_back();
        return last;
    }

private:
    // 用vector存储备忘录智能指针,自动管理内存(无需手动释放)
    vector<shared_ptr<Memento>> mementos_;
};

// -------------------------- 测试代码(客户端) --------------------------
int main() {
    // 1. 创建发起人、管理者
    TextEditor editor;
    Caretaker caretaker;

    // 2. 编辑文本并保存状态
    cout << "=== 第一次编辑 ===" << endl;
    editor.edit("Hello ");
    caretaker.pushMemento(editor.save()); // 保存状态1

    cout << "\n=== 第二次编辑 ===" << endl;
    editor.edit("World! ");
    caretaker.pushMemento(editor.save()); // 保存状态2

    cout << "\n=== 第三次编辑 ===" << endl;
    editor.edit("This is Memento Pattern.");
    caretaker.pushMemento(editor.save()); // 保存状态3

    // 3. 撤销操作
    cout << "\n=== 第一次撤销 ===" << endl;
    auto m1 = caretaker.popMemento();
    if (m1) editor.restore(m1);

    cout << "\n=== 第二次撤销 ===" << endl;
    auto m2 = caretaker.popMemento();
    if (m2) editor.restore(m2);

    cout << "\n=== 第三次撤销 ===" << endl;
    auto m3 = caretaker.popMemento();
    if (m3) editor.restore(m3);

    cout << "\n=== 第四次撤销 ===" << endl;
    caretaker.popMemento();

    return 0; // 智能指针自动销毁所有对象,无需手动释放
}

实现关键要点

1.封装性(友元机制):Memento的构造函数和状态成员均为私有,仅通过friend class TextEditor允许发起人访问,外部(包括管理者)无法修改或直接访问备忘录状态,严格遵循封装原则。
2.智能指针管理内存:用shared_ptr管理备忘录,自动处理生命周期,避免内存泄漏(管理者的vector存储智能指针,无需手动释放备忘录)。
3.状态不可变性:备忘录无setter方法,一旦创建无法修改,保证恢复状态的准确性。
4.角色职责分离:
发起人:专注于文本编辑和状态的保存 / 恢复,不管理备忘录存储;
管理者:用vector模拟栈结构,仅负责备忘录的添加 / 弹出,不感知状态细节;
备忘录:仅存储状态,无业务逻辑。
5.扩展性强:新增发起人状态(如选中区域select_range)时,仅需修改Memento的私有成员、发起人的save和restore方法,管理者无需改动,符合开闭原则。
6.类型安全:C++ 的编译期类型检查和智能指针,避免 C 语言中不透明指针的类型转换风险,代码更可靠。

备忘录模式核心总结(C vs C++)

在这里插入图片描述

设计原则

封装原则:备忘录的状态仅允许发起人访问,外部(包括管理者)不可修改;
单一职责原则:发起人管业务 + 状态读写,备忘录管状态存储,管理者管备忘录管理;
开闭原则:新增状态或扩展备忘录时,无需修改管理者,仅需调整发起人和备忘录。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值