Python脚本备忘录模式实现撤销功能:从原理到实战的完整指南
目录导读
什么是备忘录模式?核心概念解析
备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

三个核心角色:
- 发起人(Originator):需要被保存状态的对象,提供创建备忘录和恢复状态的方法
- 备忘录(Memento):存储发起人内部状态的对象,对外部不可变
- 管理者(Caretaker):负责保存和管理备忘录,但不能修改备忘录内容
这种模式完美解决了“撤销”操作的核心需求:保存历史状态 → 回退到历史状态。
为什么撤销功能需要备忘录模式?
问题场景: 假设你正在开发一个文本编辑器,用户输入了10行文字后,想撤销到第5行的状态,如果不设计模式,你可能需要:
- 每次都保存整个对象的深拷贝(内存爆炸)
- 或者记录所有操作日志(性能开销大)
备忘录模式的优势:
| 特性 | 传统方法 | 备忘录模式 |
|---|---|---|
| 封装性 | 暴露内部状态 | 状态对外隐藏 |
| 内存效率 | 每次都全拷贝 | 可增量保存 |
| 扩展性 | 修改后需重写逻辑 | 增加新状态类型容易 |
| 复杂度 | 线性增加 | 恒定复杂度 |
核心思想: 备忘录将状态的“保存”和“恢复”职责从业务逻辑中分离出来,符合单一职责原则。
Python实现备忘录模式的三种姿势
经典嵌套类实现
class TextEditor:
class Memento:
def __init__(self, state):
self._state = state
def get_state(self):
return self._state
def __init__(self):
self._text = ""
def write(self, text):
self._text = text
def save(self):
return self.Memento(self._text)
def restore(self, memento):
self._text = memento.get_state()
# 管理者
class History:
def __init__(self):
self._mementos = []
def push(self, memento):
self._mementos.append(memento)
def pop(self):
return self._mementos.pop()
使用字典/类实现(更灵活)
from copy import deepcopy
class EditorState:
def __init__(self, content, cursor_position):
self.content = content
self.cursor_position = cursor_position
class Memento:
def __init__(self, state):
self._state = deepcopy(state) # 深拷贝防止引用问题
def get_snapshot(self):
return deepcopy(self._state)
装饰器模式增强
import functools
def undoable(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
# 保存当前状态
memento = self.create_memento()
self.history.push(memento)
# 执行操作
result = func(self, *args, **kwargs)
return result
return wrapper
选择建议: 小型项目用方式一,需要序列化的场景用方式二,框架开发用方式三。
实战案例:文本编辑器撤销功能
我们来实现一个支持撤销/重做的简易文本编辑器:
class TextEditor:
def __init__(self):
self._content = []
self._cursor = 0
self._undo_stack = []
self._redo_stack = []
class Snapshot:
def __init__(self, content, cursor):
self.content = content.copy()
self.cursor = cursor
def insert(self, char):
# 保存当前状态到撤销栈
self._undo_stack.append(self.Snapshot(self._content, self._cursor))
self._content.insert(self._cursor, char)
self._cursor += 1
# 清除重做栈(新操作使重做失效)
self._redo_stack.clear()
def undo(self):
if not self._undo_stack:
return False
# 保存当前状态到重做栈
self._redo_stack.append(self.Snapshot(self._content, self._cursor))
snp = self._undo_stack.pop()
self._content = snp.content
self._cursor = snp.cursor
return True
def redo(self):
if not self._redo_stack:
return False
# 保存当前状态到撤销栈
self._undo_stack.append(self.Snapshot(self._content, self._cursor))
snp = self._redo_stack.pop()
self._content = snp.content
self._cursor = snp.cursor
return True
def __str__(self):
return ''.join(self._content)
# 使用示例
editor = TextEditor()
editor.insert('H'); editor.insert('e'); editor.insert('l')
editor.insert('l'); editor.insert('o')
print(editor) # Hello
editor.undo(); editor.undo() # 撤销两次
print(editor) # Hel
editor.redo() # 重做一次
print(editor) # Hell
进阶技巧:状态快照与性能优化
1 增量保存策略
当对象状态很大时(如100MB的文档),完整保存每个版本会耗尽内存,解决方案:
class IncrementalMemento:
def __init__(self, changes):
self.changes = changes # 只保存差异(例如插入/删除操作)
def apply(self, state):
# 把差异应用到当前状态
pass
2 状态压缩
import zlib
class CompressedMemento:
def __init__(self, state_str):
self.compressed = zlib.compress(state_str.encode())
def get_state(self):
return zlib.decompress(self.compressed).decode()
3 限制历史深度
class BoundedHistory:
def __init__(self, max_size=10):
self._max = max_size
self._mementos = []
def push(self, memento):
if len(self._mementos) >= self._max:
self._mementos.pop(0) # 移除最旧的
self._mementos.append(memento)
常见问题与解答
Q1:备忘录模式会不会暴露对象内部状态?
A:不会,备忘录类通常设计为只有发起人能访问其细节,在Python中可以通过类名前缀或实现名称修饰,更严格的做法是使用@property只读属性。
Q2:如何处理大型对象的备忘录? A:三种策略:1) 深拷贝(开销大但简单);2) 序列化(支持持久化);3) 增量快照(记录操作日志),对于大于10MB的对象,推荐使用增量方式。
Q3:撤销和重做栈如何协同工作? A:经典策略是:每次新操作将当前状态压入撤销栈,清空重做栈,撤销时:将当前状态压入重做栈,从撤销栈弹出状态恢复,重做则相反。
Q4:在分布式系统中如何使用? A:备忘录需要序列化(JSON/Pickle),管理者需要关注版本号和时间戳,防止并发冲突,可使用Redis等内存数据库存储备忘录。
Q5:备忘录模式与命令模式的区别? A:命令模式记录“操作指令”,备忘录模式记录“状态快照”,命令模式适合操作可逆的场景,备忘录适合状态可恢复的场景。
总结与最佳实践
备忘录模式为Python脚本的撤销功能提供了优雅的解决方案,它通过状态快照和历史栈管理实现了:
- 完全封装性(不破坏对象封装)
- 灵活的恢复策略(支持多次撤销/重做)
- 可扩展的状态存储(内存、文件、数据库)
推荐实践步骤
- 识别需要撤销的对象:通常是有状态的对象(如编辑器、画布、表单)
- 设计状态数据结构:选择深拷贝、序列化或增量方式
- 实现备忘录类:只提供状态获取,不提供修改
- 实现管理者类:集成栈管理、历史上限、持久化
- 集成到业务逻辑:在关键操作前保存状态
代码模板(可直接使用)
class UndoableMixin:
def __init__(self):
self._undo_stack = []
self._redo_stack = []
def _save_state(self):
self._undo_stack.append(self._create_memento())
self._redo_stack.clear()
def _create_memento(self):
# 子类需要实现
raise NotImplementedError
def undo(self):
if self._undo_stack:
self._redo_stack.append(self._create_memento())
memento = self._undo_stack.pop()
self._restore_from_memento(memento)
def redo(self):
if self._redo_stack:
self._undo_stack.append(self._create_memento())
memento = self._redo_stack.pop()
self._restore_from_memento(memento)
最后提醒:不要把备忘录模式当作万能药,对于简单的撤销需求(比如只撤销上一步),使用简单变量保存状态可能更高效,备忘录模式在你需要多级撤销、状态持久化、复杂对象恢复时才真正展现出威力。