行为型模式:⑥备忘录模式(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++)

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

被折叠的 条评论
为什么被折叠?



