备忘录模式(Memento Pattern)
备忘录模式(Memento Pattern)
1. 知识点总结
备忘录模式是一种行为型设计模式,核心思想是:
在不破坏对象封装性的前提下,捕获对象的内部状态并保存在外部,以便后续能够将对象恢复到之前的状态。
它最常见的应用就是撤销(Undo)/重做(Redo)功能,比如文本编辑器、游戏存档、数据库事务回滚等。
模式中有三个核心角色:
| 角色 | 职责 |
|---|---|
| Originator(发起人) | 拥有需要保存的状态,提供创建备忘录和恢复状态的方法 |
| Memento(备忘录) | 封装 Originator 的内部状态,对外隐藏细节 |
| Caretaker(管理者) | 负责保存和恢复备忘录,不直接操作备忘录内容 |
适用场景
- 需要实现撤销/重做功能;
- 需要保存对象的历史快照,支持回滚;
- 事务执行失败时需要还原到执行前的状态。
优缺点
| 优点 | 缺点 |
|---|---|
| 不破坏封装性,外部无法随意修改备忘录状态 | 频繁保存大对象会消耗较多内存 |
| 状态保存逻辑与业务逻辑分离 | 管理者需要维护备忘录集合,可能引入额外复杂度 |
| 容易实现多步撤销/重做 | 需要谨慎处理深拷贝与浅拷贝问题 |
2. 示例代码
下面用一个简单的文本编辑器来演示:每次写入后保存快照,误操作后可以通过历史栈撤销。
package main
import "fmt"
// Editor 是发起人(Originator),拥有需要被保存的内部状态。
type Editor struct {
content string
}
func (e *Editor) Write(text string) {
e.content += text
}
func (e *Editor) Content() string {
return e.content
}
// Save 创建一份备忘录,封装当前内容。
func (e *Editor) Save() Memento {
return Memento{snapshot: e.content}
}
// Restore 根据备忘录恢复状态。
func (e *Editor) Restore(m Memento) {
e.content = m.snapshot
}
// Memento 是备忘录,只暴露给 Caretaker 和 Originator 使用。
// 这里通过未导出字段限制外部直接修改快照内容。
type Memento struct {
snapshot string
}
// History 是管理者(Caretaker),负责维护备忘录的历史栈。
type History struct {
stack []Memento
}
func (h *History) Save(m Memento) {
h.stack = append(h.stack, m)
}
func (h *History) Undo() (Memento, bool) {
if len(h.stack) == 0 {
return Memento{}, false
}
last := h.stack[len(h.stack)-1]
h.stack = h.stack[:len(h.stack)-1]
return last, true
}
func main() {
editor := &Editor{}
history := &History{}
editor.Write("Hello ")
history.Save(editor.Save())
fmt.Printf("写入后: %q\n", editor.Content())
editor.Write("World!")
history.Save(editor.Save())
fmt.Printf("再次写入后: %q\n", editor.Content())
editor.Write(" 这是多余内容")
fmt.Printf("误操作后: %q\n", editor.Content())
if m, ok := history.Undo(); ok {
editor.Restore(m)
fmt.Printf("撤销一次后: %q\n", editor.Content())
}
if m, ok := history.Undo(); ok {
editor.Restore(m)
fmt.Printf("再撤销一次后: %q\n", editor.Content())
}
}
3. 运行结果
写入后: "Hello "
再次写入后: "Hello World!"
误操作后: "Hello World! 这是多余内容"
撤销一次后: "Hello World!"
再撤销一次后: "Hello "
4. 关键点复盘
- 封装性是第一原则:备忘录的
snapshot字段没有导出,外部(包括 Caretaker)不能直接修改快照内容,只能通过 Originator 的Save/Restore来操作。 - Caretaker 只负责保管:
History不知道备忘录里具体存了什么,也不应该去解析它,这样 Originator 的内部结构变化不会影响历史管理逻辑。 - 栈是实现撤销的常用结构:每次保存时入栈,撤销时出栈并恢复;如果要支持重做,可以再加一个 redo 栈。
- 注意深拷贝:如果 Originator 的状态包含引用类型(切片、Map、指针),保存备忘录时需要进行深拷贝,否则快照会被后续修改污染。
- 与命令模式的区别:备忘录模式通过保存状态快照来撤销;命令模式通常通过记录逆操作来撤销。状态简单时用备忘录更直接,操作复杂时命令模式更灵活。

浙公网安备 33010602011771号