备忘录模式

备忘录模式(Memento Pattern)是行为型设计模式的重要分支,其核心设计目标是在不破坏对象封装性的前提下,捕获对象的内部状态并将其持久化到外部容器,以便在需要时快速将对象恢复至指定历史状态。该模式完美解决了“状态保存与恢复”的核心痛点,广泛应用于撤销操作、数据回滚、快照备份等场景。本文将从核心结构拆解、多语言落地实现、优缺点深度剖析、适用场景梳理及实战总结等维度,系统解读备忘录模式的设计思想与工程实践,为开发者提供可直接复用的技术方案。

一、备忘录模式核心结构

备忘录模式的核心价值在于“封装状态、解耦管理”,通过三大核心角色的协同工作,实现状态的安全保存与灵活恢复,各角色职责边界清晰,无冗余依赖,具体定义与交互逻辑如下:

1.1 原发器(Originator)

原发器是需要进行状态管理的核心业务对象,负责两个核心操作:一是创建备忘录,即捕获自身当前的内部状态,并将状态写入备忘录对象;二是恢复状态,即从备忘录中读取历史状态,将自身恢复至该状态。原发器是唯一有权限访问备忘录内部状态的角色,确保状态的封装性。

1.2 备忘录(Memento)

备忘录是状态的“容器”,专门用于存储原发器的内部状态,其设计核心是“封装隔离”。为保证状态安全,备忘录仅暴露供原发器读写状态的接口(或仅允许原发器访问其私有状态),其他角色(如管理者)仅能持有备忘录对象,无法修改或直接访问其内部状态,避免破坏原发器的封装性。

1.3 管理者(Caretaker)

管理者是备忘录的“管家”,负责备忘录对象的生命周期管理,包括存储、获取、清理等操作,但不参与状态的读写。管理者的核心作用是解耦原发器与备忘录的直接依赖,通过统一的接口管理多个备忘录,支持多状态快照的存储与切换(如多步撤销)。

核心交互流程:原发器捕获当前状态 → 创建备忘录并写入状态 → 管理者存储备忘录 → 需恢复状态时,管理者取出指定备忘录 → 原发器从备忘录读取状态并完成恢复。

二、多语言实现备忘录模式

以“文本编辑器撤销功能”为统一实战场景,设计核心逻辑:原发器(编辑器)负责存储当前文本内容,备忘录存储文本快照,管理者管理快照列表,支持多步保存与撤销。以下提供C#、Python、Golang、C++及纯C的可运行实现,贴合各语言原生特性,补充规范注释、边界校验与资源释放逻辑,兼顾实用性与严谨性,清晰呈现模式的适配思路。

2.1 C# 实现(面向对象规范实现)

C#作为强类型面向对象语言,通过类封装备忘录状态,依托访问修饰符(private)保证备忘录的封装性,借助泛型与集合优化管理者的备忘录管理逻辑,代码结构清晰、可维护性高,适用于企业级应用的撤销/回滚场景(如办公软件、业务系统配置管理)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using System;
using System.Collections.Generic;

/// <summary>
/// 备忘录:存储原发器(文本编辑器)的状态,仅允许原发器访问
/// </summary>
public class Memento
{
/// <summary>
/// 存储的文本状态(私有字段,仅通过属性供原发器访问)
/// </summary>
public string TextState { get; private set; }

/// <summary>
/// 构造函数:仅允许原发器创建备忘录(通过访问修饰符控制)
/// </summary>
/// <param name="textState">文本编辑器当前状态</param>
internal Memento(string textState)
{
TextState = textState ?? string.Empty; // 避免空值,提升健壮性
}
}

/// <summary>
/// 原发器:文本编辑器,负责创建/恢复备忘录,管理自身文本状态
/// </summary>
public class Originator
{
/// <summary>
/// 编辑器当前文本状态
/// </summary>
private string _currentText = string.Empty;

/// <summary>
/// 设置编辑器文本状态
/// </summary>
/// <param name="text">新文本内容</param>
public void SetText(string text)
{
_currentText = text ?? string.Empty;
Console.WriteLine($"编辑器更新状态:{_currentText}");
}

/// <summary>
/// 获取当前文本状态
/// </summary>
/// <returns>当前文本内容</returns>
public string GetText() => _currentText;

/// <summary>
/// 创建备忘录:捕获当前文本状态,生成快照
/// </summary>
/// <returns>包含当前状态的备忘录</returns>
public Memento CreateMemento()
{
return new Memento(_currentText);
}

/// <summary>
/// 从备忘录恢复状态:将编辑器恢复至备忘录存储的历史状态
/// </summary>
/// <param name="memento">待恢复的备忘录</param>
public void RestoreMemento(Memento memento)
{
if (memento == null)
{
Console.WriteLine("警告:备忘录不可为null,无法恢复状态");
return;
}
_currentText = memento.TextState;
Console.WriteLine($"编辑器恢复状态:{_currentText}");
}
}

/// <summary>
/// 管理者:管理备忘录列表,支持多快照存储与获取
/// </summary>
public class Caretaker
{
/// <summary>
/// 存储备忘录的队列(FIFO,适配多步撤销场景)
/// </summary>
private readonly Queue<Memento> _mementos = new Queue<Memento>();

/// <summary>
/// 最大快照数量(避免内存溢出)
/// </summary>
private readonly int _maxCount = 10;

/// <summary>
/// 添加备忘录快照
/// </summary>
/// <param name="memento">待存储的备忘录</param>
public void AddMemento(Memento memento)
{
if (memento == null)
{
Console.WriteLine("警告:无法添加空备忘录");
return;
}
// 超过最大快照数量,移除最旧的快照
if (_mementos.Count >= _maxCount)
{
_mementos.Dequeue();
Console.WriteLine("提示:快照数量已达上限,移除最旧快照");
}
_mementos.Enqueue(memento);
Console.WriteLine($"快照添加成功,当前快照数量:{_mementos.Count}");
}

/// <summary>
/// 获取最新的备忘录快照(适配一步撤销)
/// </summary>
/// <returns>最新的备忘录,无快照时返回null</returns>
public Memento GetLatestMemento()
{
if (_mementos.Count == 0)
{
Console.WriteLine("警告:无历史快照可获取");
return null;
}
// 队列尾部为最新快照,此处采用ToArray获取最后一个元素(不改变队列结构)
var mementoArray = _mementos.ToArray();
return mementoArray[^1];
}

/// <summary>
/// 根据索引获取指定备忘录快照(适配多步撤销)
/// </summary>
/// <param name="index">快照索引</param>
/// <returns>指定索引的备忘录,索引无效时返回null</returns>
public Memento GetMemento(int index)
{
if (index < 0 || index >= _mementos.Count)
{
Console.WriteLine("警告:快照索引无效");
return null;
}
return _mementos.ToArray()[index];
}
}

// 客户端:测试文本编辑器的撤销功能
class Program
{
static void Main()
{
try
{
// 1. 初始化组件
Originator editor = new Originator();
Caretaker caretaker = new Caretaker();

// 2. 输入文本并保存快照
editor.SetText("第1步:编写备忘录模式核心概念");
caretaker.AddMemento(editor.CreateMemento());

// 3. 继续输入并保存快照
editor.SetText("第2步:梳理多语言实现思路");
caretaker.AddMemento(editor.CreateMemento());

// 4. 继续输入并保存快照
editor.SetText("第3步:分析优缺点与使用场景");
caretaker.AddMemento(editor.CreateMemento());

// 5. 撤销一步(恢复到第2步状态)
Console.WriteLine("\n=== 执行一步撤销 ===");
editor.RestoreMemento(caretaker.GetLatestMemento());

// 6. 再撤销一步(恢复到第1步状态)
Console.WriteLine("\n=== 执行第二步撤销 ===");
editor.RestoreMemento(caretaker.GetMemento(0));
}
catch (Exception ex)
{
Console.WriteLine($"执行异常:{ex.Message}");
}
}
}

2.2 Python 实现(动态语言简洁实现)

Python遵循“鸭子类型”,无需显式定义接口,通过类封装与属性装饰器(@property)保证备忘录的封装性,语法简洁、无冗余类型声明。依托动态特性,可灵活扩展备忘录的状态字段,无需修改核心逻辑,适用于快速开发、轻量级项目(如小型编辑器、配置工具)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Memento:
"""备忘录:存储文本编辑器的状态,仅允许原发器访问内部状态"""
def __init__(self, text_state):
# 私有字段,通过属性供原发器访问,外部无法直接修改
self._text_state = text_state or ""

@property
def text_state(self):
"""供原发器读取状态的接口"""
return self._text_state

class Originator:
"""原发器:文本编辑器,负责创建/恢复备忘录,管理自身状态"""
def __init__(self):
self._current_text = "" # 编辑器当前文本状态

def set_text(self, text):
"""设置编辑器文本状态,处理空值,提升健壮性"""
self._current_text = text or ""
print(f"编辑器更新状态:{self._current_text}")

def get_text(self):
"""获取当前文本状态"""
return self._current_text

def create_memento(self):
"""创建备忘录,捕获当前文本状态"""
return Memento(self._current_text)

def restore_memento(self, memento):
"""从备忘录恢复状态,校验备忘录合法性"""
if not isinstance(memento, Memento):
print("警告:无效的备忘录对象,无法恢复状态")
return
self._current_text = memento.text_state
print(f"编辑器恢复状态:{self._current_text}")

class Caretaker:
"""管理者:管理备忘录列表,支持多快照存储与多步撤销"""
def __init__(self, max_count=10):
self._mementos = [] # 存储备忘录的列表
self._max_count = max_count # 最大快照数量,避免内存溢出

def add_memento(self, memento):
"""添加备忘录快照,处理空值与数量上限"""
if not isinstance(memento, Memento):
print("警告:无法添加无效的备忘录")
return
# 超过最大数量,移除最旧的快照
if len(self._mementos) >= self._max_count:
self._mementos.pop(0)
print("提示:快照数量已达上限,移除最旧快照")
self._mementos.append(memento)
print(f"快照添加成功,当前快照数量:{len(self._mementos)}")

def get_latest_memento(self):
"""获取最新的备忘录快照(一步撤销)"""
if not self._mementos:
print("警告:无历史快照可获取")
return None
return self._mementos[-1]

def get_memento(self, index):
"""根据索引获取指定备忘录快照(多步撤销)"""
if index < 0 or index >= len(self._mementos):
print("警告:快照索引无效")
return None
return self._mementos[index]

# 客户端测试:模拟文本编辑器的撤销流程
if __name__ == "__main__":
try:
# 1. 初始化组件
editor = Originator()
caretaker = Caretaker(max_count=5)

# 2. 输入文本并保存快照
editor.set_text("第1步:编写备忘录模式核心概念")
caretaker.add_memento(editor.create_memento())

# 3. 继续输入并保存快照
editor.set_text("第2步:梳理多语言实现思路")
caretaker.add_memento(editor.create_memento())

# 4. 继续输入并保存快照
editor.set_text("第3步:分析优缺点与使用场景")
caretaker.add_memento(editor.create_memento())

# 5. 撤销一步(恢复到第2步状态)
print("\n=== 执行一步撤销 ===")
editor.restore_memento(caretaker.get_latest_memento())

# 6. 再撤销一步(恢复到第1步状态)
print("\n=== 执行第二步撤销 ===")
editor.restore_memento(caretaker.get_memento(0))
except Exception as e:
print(f"执行异常:{str(e)}")

2.3 Golang 实现(接口至上轻量实现)

Go语言无类和继承,遵循“组合优于继承”原则,通过结构体封装备忘录与原发器状态,依托“小写字段”实现封装(包内可见、包外不可访问),无需额外访问修饰符。代码极简高效,贴合Go语言设计理念,适配高并发后端场景(如分布式系统配置回滚、服务状态快照)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main

import (
"fmt"
)

// memento 备忘录:小写字段保证包内访问(封装),仅允许原发器访问状态
type memento struct {
textState string // 存储的文本状态
}

// Originator 原发器:文本编辑器,负责创建/恢复备忘录
type Originator struct {
currentText string // 编辑器当前文本状态
}

// SetText 设置编辑器文本状态,处理空值
func (o *Originator) SetText(text string) {
if text == "" {
o.currentText = ""
} else {
o.currentText = text
}
fmt.Printf("编辑器更新状态:%s\n", o.currentText)
}

// GetText 获取当前文本状态
func (o *Originator) GetText() string {
return o.currentText
}

// CreateMemento 创建备忘录,捕获当前状态
func (o *Originator) CreateMemento() *memento {
return &memento{textState: o.currentText}
}

// RestoreMemento 从备忘录恢复状态,校验备忘录合法性
func (o *Originator) RestoreMemento(m *memento) {
if m == nil {
fmt.Println("警告:备忘录不可为nil,无法恢复状态")
return
}
o.currentText = m.textState
fmt.Printf("编辑器恢复状态:%s\n", o.currentText)
}

// Caretaker 管理者:管理备忘录列表,支持多快照存储与撤销
type Caretaker struct {
mementos []*memento // 存储备忘录的切片
maxCount int // 最大快照数量
}

// NewCaretaker 初始化管理者,指定最大快照数量
func NewCaretaker(maxCount int) *Caretaker {
if maxCount <= 0 {
maxCount = 10 // 默认最大快照数量
}
return &Caretaker{
mementos: make([]*memento, 0, maxCount),
maxCount: maxCount,
}
}

// AddMemento 添加备忘录快照,处理数量上限
func (c *Caretaker) AddMemento(m *memento) {
if m == nil {
fmt.Println("警告:无法添加空备忘录")
return
}
// 超过最大数量,移除最旧的快照
if len(c.mementos) >= c.maxCount {
c.mementos = c.mementos[1:]
fmt.Println("提示:快照数量已达上限,移除最旧快照")
}
c.mementos = append(c.mementos, m)
fmt.Printf("快照添加成功,当前快照数量:%d\n", len(c.mementos))
}

// GetLatestMemento 获取最新的备忘录快照(一步撤销)
func (c *Caretaker) GetLatestMemento() *memento {
if len(c.mementos) == 0 {
fmt.Println("警告:无历史快照可获取")
return nil
}
return c.mementos[len(c.mementos)-1]
}

// GetMemento 根据索引获取指定备忘录快照(多步撤销)
func (c *Caretaker) GetMemento(index int) *memento {
if index < 0 || index >= len(c.mementos) {
fmt.Println("警告:快照索引无效")
return nil
}
return c.mementos[index]
}

// 客户端测试:模拟文本编辑器撤销流程
func main() {
// 1. 初始化组件
editor := &Originator{}
caretaker := NewCaretaker(5)

// 2. 输入文本并保存快照
editor.SetText("第1步:编写备忘录模式核心概念")
caretaker.AddMemento(editor.CreateMemento())

// 3. 继续输入并保存快照
editor.SetText("第2步:梳理多语言实现思路")
caretaker.AddMemento(editor.CreateMemento())

// 4. 继续输入并保存快照
editor.SetText("第3步:分析优缺点与使用场景")
caretaker.AddMemento(editor.CreateMemento())

// 5. 撤销一步(恢复到第2步状态)
fmt.Println("\n=== 执行一步撤销 ===")
editor.RestoreMemento(caretaker.GetLatestMemento())

// 6. 再撤销一步(恢复到第1步状态)
fmt.Println("\n=== 执行第二步撤销 ===")
editor.RestoreMemento(caretaker.GetMemento(0))
}

2.4 C++ 实现(抽象类+友元经典实现)

C++通过类与友元机制实现备忘录的封装性,将原发器声明为备忘录的友元,确保只有原发器能访问备忘录的私有状态;同时通过虚析构函数与手动内存管理,避免内存泄漏。该实现兼顾灵活性与高性能,适用于底层开发、高频调用场景(如编译器撤销功能、嵌入式设备配置回滚)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 前向声明:让管理者识别备忘录
class Memento;

/// <summary>
/// 原发器:文本编辑器,负责创建/恢复备忘录
/// 声明为Memento的友元,有权访问其私有状态
/// </summary>
class Originator {
private:
string currentText; // 编辑器当前文本状态
public:
Originator() : currentText("") {}

/// <summary>
/// 设置编辑器文本状态
/// </summary>
void setText(const string& text) {
currentText = text.empty() ? "" : text;
cout << "编辑器更新状态:" << currentText << endl;
}

/// <summary>
/// 获取当前文本状态
/// </summary>
string getText() const {
return currentText;
}

/// <summary>
/// 创建备忘录,捕获当前状态
/// </summary>
Memento* createMemento();

/// <summary>
/// 从备忘录恢复状态
/// </summary>
void restoreMemento(Memento* m);
};

/// <summary>
/// 备忘录:存储文本状态,仅允许原发器访问私有成员
/// </summary>
class Memento {
private:
string textState; // 存储的文本状态
// 友元声明:仅Originator可访问私有成员
friend class Originator;

/// <summary>
/// 私有构造函数:仅允许原发器创建备忘录
/// </summary>
Memento(const string& state) : textState(state) {}

/// <summary>
/// 私有接口:供原发器读取状态
/// </summary>
string getTextState() const {
return textState;
}
};

// 实现原发器的备忘录方法
Memento* Originator::createMemento() {
return new Memento(currentText);
}

void Originator::restoreMemento(Memento* m) {
if (m == nullptr) {
cout << "警告:备忘录不可为nullptr,无法恢复状态" << endl;
return;
}
currentText = m->getTextState();
cout << "编辑器恢复状态:" << currentText << endl;
}

/// <summary>
/// 管理者:管理备忘录列表,负责内存释放
/// </summary>
class Caretaker {
private:
vector<Memento*> mementos; // 存储备忘录指针的容器
int maxCount; // 最大快照数量
public:
Caretaker(int maxCount = 10) : maxCount(maxCount) {}

/// <summary>
/// 析构函数:释放所有备忘录内存,避免泄漏
/// </summary>
~Caretaker() {
for (auto m : mementos) {
delete m;
m = nullptr;
}
mementos.clear();
}

/// <summary>
/// 添加备忘录快照
/// </summary>
void addMemento(Memento* m) {
if (m == nullptr) {
cout << "警告:无法添加空备忘录" << endl;
return;
}
// 超过最大数量,移除最旧的快照并释放内存
if (mementos.size() >= maxCount) {
delete mementos[0];
mementos[0] = nullptr;
mementos.erase(mementos.begin());
cout << "提示:快照数量已达上限,移除最旧快照" << endl;
}
mementos.push_back(m);
cout << "快照添加成功,当前快照数量:" << mementos.size() << endl;
}

/// <summary>
/// 获取最新的备忘录快照
/// </summary>
Memento* getLatestMemento() {
if (mementos.empty()) {
cout << "警告:无历史快照可获取" << endl;
return nullptr;
}
return mementos.back();
}

/// <summary>
/// 根据索引获取指定备忘录快照
/// </summary>
Memento* getMemento(int index) {
if (index < 0 || index >= (int)mementos.size()) {
cout << "警告:快照索引无效" << endl;
return nullptr;
}
return mementos[index];
}
};

// 客户端测试:模拟文本编辑器撤销流程
int main() {
try {
// 1. 初始化组件
Originator* editor = new Originator();
Caretaker* caretaker = new Caretaker(5);

// 2. 输入文本并保存快照
editor->setText("第1步:编写备忘录模式核心概念");
caretaker->addMemento(editor->createMemento());

// 3. 继续输入并保存快照
editor->setText("第2步:梳理多语言实现思路");
caretaker->addMemento(editor->createMemento());

// 4. 继续输入并保存快照
editor->setText("第3步:分析优缺点与使用场景");
caretaker->addMemento(editor->createMemento());

// 5. 撤销一步(恢复到第2步状态)
cout << "\n=== 执行一步撤销 ===" << endl;
editor->restoreMemento(caretaker->getLatestMemento());

// 6. 再撤销一步(恢复到第1步状态)
cout << "\n=== 执行第二步撤销 ===" << endl;
editor->restoreMemento(caretaker->getMemento(0));

// 释放资源
delete editor;
delete caretaker;
editor = nullptr;
caretaker = nullptr;
} catch (const exception& e) {
cout << "执行异常:" << e.what() << endl;
}
return 0;
}

2.5 纯C语言实现(结构体+函数指针模拟实现)

纯C无面向对象特性,通过“结构体封装数据+函数指针封装行为”模拟备忘录模式的核心逻辑,依托命名约定(前缀下划线)模拟私有字段,通过函数接口控制状态的读写权限,底层可控性强、无额外依赖。适用于嵌入式、底层开发等资源受限场景,完整还原模式“封装状态、解耦管理”的核心思想。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 前向声明:解决结构体与函数指针的交叉引用
typedef struct Memento Memento;
typedef struct Originator Originator;
typedef struct Caretaker Caretaker;

/// <summary>
/// 备忘录结构体:模拟私有字段(下划线前缀),仅通过接口供原发器访问
/// </summary>
typedef struct Memento {
char* _text_state; // 存储的文本状态(模拟私有字段)
} Memento;

/// <summary>
/// 原发器结构体:文本编辑器,封装状态与方法指针
/// </summary>
typedef struct Originator {
char* current_text; // 当前文本状态
// 方法指针:模拟类的成员方法
Memento* (*create_memento)(Originator*); // 创建备忘录
void (*restore_memento)(Originator*, Memento*); // 恢复状态
} Originator;

/// <summary>
/// 管理者结构体:管理备忘录列表
/// </summary>
typedef struct Caretaker {
Memento** mementos; // 存储备忘录指针的数组
int count; // 当前快照数量
int capacity; // 数组容量
int max_count; // 最大快照数量
} Caretaker;

// -------------------------- 备忘录工具函数 --------------------------
/// <summary>
/// 创建备忘录,初始化状态
/// </summary>
Memento* memento_create(const char* text_state) {
Memento* m = (Memento*)malloc(sizeof(Memento));
if (m == NULL) return NULL;
// 处理空值,避免野指针
int len = text_state ? strlen(text_state) : 0;
m->_text_state = (char*)malloc(len + 1);
if (m->_text_state == NULL) {
free(m);
return NULL;
}
strcpy(m->_text_state, text_state ? text_state : "");
return m;
}

/// <summary>
/// 销毁备忘录,释放内存
/// </summary>
void memento_destroy(Memento* m) {
if (m == NULL) return;
free(m->_text_state);
free(m);
}

/// <summary>
/// 供原发器读取备忘录状态的接口(模拟私有字段访问)
/// </summary>
const char* memento_get_text_state(Memento* m) {
if (m == NULL) return "";
return m->_text_state;
}

// -------------------------- 原发器方法实现 --------------------------
/// <summary>
/// 原发器:创建备忘录,捕获当前状态
/// </summary>
Memento* originator_create_memento(Originator* o) {
if (o == NULL) return NULL;
return memento_create(o->current_text);
}

/// <summary>
/// 原发器:从备忘录恢复状态
/// </summary>
void originator_restore_memento(Originator* o, Memento* m) {
if (o == NULL || m == NULL) {
printf("警告:原发器或备忘录为空,无法恢复状态\n");
return;
}
// 释放原有状态内存,避免泄漏
if (o->current_text != NULL) {
free(o->current_text);
}
// 复制备忘录中的状态
const char* state = memento_get_text_state(m);
int len = strlen(state);
o->current_text = (char*)malloc(len + 1);
strcpy(o->current_text, state);
printf("编辑器恢复状态:%s\n", o->current_text);
}

/// <summary>
/// 原发器:设置文本状态
/// </summary>
void originator_set_text(Originator* o, const char* text) {
if (o == NULL) return;
// 释放原有状态内存
if (o->current_text != NULL) {
free(o->current_text);
}
// 处理空值
int len = text ? strlen(text) : 0;
o->current_text = (char*)malloc(len + 1);
strcpy(o->current_text, text ? text : "");
printf("编辑器更新状态:%s\n", o->current_text);
}

/// <summary>
/// 初始化原发器
/// </summary>
Originator* originator_init() {
Originator* o = (Originator*)malloc(sizeof(Originator));
if (o == NULL) return NULL;
o->current_text = NULL;
o->create_memento = originator_create_memento;
o->restore_memento = originator_restore_memento;
// 初始化状态为空字符串
originator_set_text(o, "");
return o;
}

// -------------------------- 管理者方法实现 --------------------------
/// <summary>
/// 初始化管理者
/// </summary>
Caretaker* caretaker_init(int max_count) {
Caretaker* c = (Caretaker*)malloc(sizeof(Caretaker));
if (c == NULL) return NULL;
// 默认最大快照数量为10
c->max_count = max_count <= 0 ? 10 : max_count;
c->capacity = c->max_count;
c->count = 0;
c->mementos = (Memento**)malloc(sizeof(Memento*) * c->capacity);
if (c->mementos == NULL) {
free(c);
return NULL;
}
return c;
}

/// <summary>
/// 管理者:添加备忘录快照
/// </summary>
void caretaker_add_memento(Caretaker* c, Memento* m) {
if (c == NULL || m == NULL) {
printf("警告:管理者或备忘录为空,无法添加快照\n");
return;
}
// 超过最大数量,移除最旧快照并释放内存
if (c->count >= c->max_count) {
memento_destroy(c->mementos[0]);
// 移动数组,移除第一个元素
for (int i = 0; i < c->count - 1; i++) {
c->mementos[i] = c->mementos[i + 1];
}
c->count--;
printf("提示:快照数量已达上限,移除最旧快照\n");
}
c->mementos[c->count++] = m;
printf("快照添加成功,当前快照数量:%d\n", c->count);
}

/// <summary>
/// 管理者:获取最新的备忘录快照
/// </summary>
Memento* caretaker_get_latest_memento(Caretaker* c) {
if (c == NULL || c->count == 0) {
printf("警告:无历史快照可获取\n");
return NULL;
}
return c->mementos[c->count - 1];
}

/// <summary>
/// 管理者:根据索引获取指定备忘录快照
/// </summary>
Memento* caretaker_get_memento(Caretaker* c, int index) {
if (c == NULL || index < 0 || index >= c->count) {
printf("警告:快照索引无效\n");
return NULL;
}
return c->mementos[index];
}

/// <summary>
/// 释放所有资源
/// </summary>
void cleanup(Originator* o, Caretaker* c) {
// 释放原发器资源
if (o != NULL) {
if (o->current_text != NULL) {
free(o->current_text);
}
free(o);
}
// 释放管理者与备忘录资源
if (c != NULL) {
for (int i = 0; i < c->count; i++) {
memento_destroy(c->mementos[i]);
}
free(c->mementos);
free(c);
}
}

// 客户端测试:模拟文本编辑器撤销流程
int main() {
// 1. 初始化组件
Originator* editor = originator_init();
Caretaker* caretaker = caretaker_init(5);
if (editor == NULL || caretaker == NULL) {
printf("初始化失败,退出程序\n");
cleanup(editor, caretaker);
return -1;
}

// 2. 输入文本并保存快照
originator_set_text(editor, "第1步:编写备忘录模式核心概念");
caretaker_add_memento(caretaker, editor->create_memento(editor));

// 3. 继续输入并保存快照
originator_set_text(editor, "第2步:梳理多语言实现思路");
caretaker_add_memento(caretaker, editor->create_memento(editor));

// 4. 继续输入并保存快照
originator_set_text(editor, "第3步:分析优缺点与使用场景");
caretaker_add_memento(caretaker, editor->create_memento(editor));

// 5. 撤销一步(恢复到第2步状态)
printf("\n=== 执行一步撤销 ===\n");
editor->restore_memento(editor, caretaker_get_latest_memento(caretaker));

// 6. 再撤销一步(恢复到第1步状态)
printf("\n=== 执行第二步撤销 ===\n");
editor->restore_memento(editor, caretaker_get_memento(caretaker, 0));

// 7. 释放所有资源
cleanup(editor, caretaker);
return 0;
}

三、备忘录模式的优缺点

备忘录模式的核心优势的是“封装状态、灵活回滚”,但同时存在资源消耗与维护成本的权衡。实际开发中需结合业务场景,平衡设计优雅与工程落地成本,避免过度设计或滥用。

3.1 核心优点

  • 封装性极佳:备忘录仅允许原发器访问内部状态,外部角色(如管理者)无法直接修改或访问,完美遵循“封装原则”,保证状态数据的安全性。

  • 状态回滚灵活:支持多步快照的保存与恢复,可轻松实现撤销、回滚、快照备份等功能,无需修改原发器核心逻辑,适配多场景状态管理需求。

  • 职责划分清晰:原发器负责状态管理,备忘录负责状态存储,管理者负责备忘录生命周期管理,三者各司其职、解耦关联,符合“单一职责原则”,提升代码可维护性。

  • 扩展性良好:新增状态字段或扩展快照功能时,仅需修改原发器与备忘录,无需改动管理者,对现有业务逻辑侵入性低。

3.2 主要缺点

  • 资源消耗较大:若原发器状态字段多、快照保存频繁,会产生大量备忘录对象,占用内存资源;尤其在高频保存场景(如实时编辑器),可能引发性能瓶颈。

  • 维护成本较高:若原发器的状态结构发生变更(如新增/删除字段),备忘录需同步修改,管理者的存储逻辑也可能需要调整,增加维护成本。

  • 并发安全风险:多线程环境下,多个线程同时读写备忘录或原发器状态,可能引发线程安全问题,需额外添加锁机制,增加代码复杂度。

  • 序列化成本高:若需持久化备忘录(如存储到文件、数据库),需实现序列化/反序列化逻辑,尤其对于复杂状态,序列化成本较高。

四、备忘录模式的使用场景

备忘录模式的适用场景具有明确的核心前提:需要保存对象历史状态,且需支持状态回滚,同时要求不破坏对象封装性。以下结合具体业务场景与实战案例,帮助开发者精准判断适用性,避免滥用。

4.1 核心适用场景

  • 撤销/回滚操作场景:最典型的场景是文本编辑器、表格软件的撤销功能,每输入一步保存一个快照,支持多步撤销;此外,数据库事务回滚、代码版本回滚也属于此类场景。

  • 状态快照备份场景:如游戏存档(保存角色等级、装备、进度等状态)、虚拟机快照(保存虚拟机运行状态)、系统配置快照(保存系统当前配置),支持后续恢复至指定状态。

  • 业务流程回溯场景:如审批流程中,保存每个节点的状态,支持回溯到任意节点重新处理;订单流程中,保存订单的状态变更记录,便于异常时回滚至正常状态。

  • 测试场景:自动化测试中,保存测试用例的初始状态,执行测试后恢复至初始状态,避免测试用例之间的相互影响。

4.2 不适用场景

  • 对象状态简单、无需回滚的场景,使用备忘录模式会增加代码复杂度,直接通过变量存储状态更高效。

  • 状态频繁变更且状态量极大的场景(如实时日志、高频数据采集),大量备忘录会占用过多内存,导致性能下降。

  • 对象封装性要求极低,允许外部直接访问状态的场景,无需通过备忘录间接管理状态。

五、总结

备忘录模式通过“原发器-备忘录-管理者”的三元结构,优雅地解决了“状态保存与恢复”的核心痛点,其核心设计思想是在不破坏对象封装性的前提下,实现状态的安全存储与灵活回滚。该模式的本质是“状态的隔离与管理”,让原发器专注于核心业务逻辑,将状态的存储与恢复交给备忘录和管理者,提升代码的可维护性与扩展性。

不同语言对备忘录模式的实现风格虽有显著差异,但核心逻辑高度一致:面向对象语言(C#、Python、Golang、C++)依托类、接口、友元等特性,轻松实现状态封装与访问控制,代码结构清晰;纯C语言则通过结构体+函数指针模拟面向对象特性,虽实现繁琐,但能在资源受限场景中还原模式核心价值,兼顾底层可控性。

使用备忘录模式时,需重点权衡两个核心点:一是资源消耗,对于高频保存、状态量大的场景,可优化为增量快照(仅保存状态变化部分)或定期清理过期快照,避免内存溢出;二是维护成本,若原发器状态频繁变更,需谨慎使用,避免因状态结构调整导致的大量代码修改。

总之,备忘录模式是“状态管理与回滚”场景下的最优解决方案之一,掌握其核心结构与多语言实现,能帮助开发者在实际项目中优雅处理撤销、快照、回滚等需求,平衡代码设计与工程落地成本,提升系统的健壮性与可扩展性。