备忘录模式属于行为型设计模式的一种,它的核心作用是在不暴露对象内部实现细节的情况下,保存对象的历史状态,并且可以在需要的时候将对象恢复到之前的状态。在Golang中实现备忘录模式,需要明确三个核心角色的分工,分别是原发器、备忘录和管理者。

备忘录模式的核心角色
1. 原发器(Originator)
原发器是需要保存历史状态的对象,它内部包含了需要被保存的状态数据,同时提供了创建备忘录和恢复状态的方法。
2. 备忘录(Memento)
备忘录用于存储原发器的历史状态,它通常只暴露给原发器访问内部状态的方法,对其他对象隐藏状态细节,保证封装性。
3. 管理者(Caretaker)
管理者负责保存多个备忘录对象,它不直接操作备忘录的内部状态,只负责存储和提供备忘录。
Golang实现备忘录模式的完整示例
下面以一个简单的文本编辑器场景为例,实现用备忘录模式保存文本的历史修改记录,支持撤销操作。
第一步:定义备忘录结构体
备忘录只需要存储文本内容和对应的版本号,不对外暴露修改方法。
package main
// Memento 备忘录结构体,存储文本的历史状态
type Memento struct {
content string // 文本内容
version int // 版本号
}
// GetContent 获取备忘录中保存的文本内容,仅原发器可调用
func (m *Memento) GetContent() string {
return m.content
}
// GetVersion 获取备忘录的版本号
func (m *Memento) GetVersion() int {
return m.version
}
第二步:定义原发器结构体
原发器是文本编辑器本身,包含当前文本内容和版本号,提供创建备忘录和恢复状态的方法。
// TextEditor 原发器,代表文本编辑器
type TextEditor struct {
currentContent string // 当前文本内容
currentVersion int // 当前版本号
}
// NewTextEditor 创建新的文本编辑器实例
func NewTextEditor() *TextEditor {
return &TextEditor{
currentContent: "",
currentVersion: 0,
}
}
// WriteContent 修改文本内容,同时版本号自增
func (t *TextEditor) WriteContent(content string) {
t.currentContent = content
t.currentVersion++
}
// CreateMemento 创建当前状态的备忘录
func (t *TextEditor) CreateMemento() *Memento {
return &Memento{
content: t.currentContent,
version: t.currentVersion,
}
}
// RestoreFromMemento 从备忘录恢复状态
func (t *TextEditor) RestoreFromMemento(m *Memento) {
t.currentContent = m.GetContent()
t.currentVersion = m.GetVersion()
}
// GetCurrentContent 获取当前文本内容
func (t *TextEditor) GetCurrentContent() string {
return t.currentContent
}
// GetCurrentVersion 获取当前版本号
func (t *TextEditor) GetCurrentVersion() int {
return t.currentVersion
}
第三步:定义管理者结构体
管理者维护一个备忘录列表,负责添加和获取备忘录,这里实现简单的撤销逻辑,获取上一个版本的备忘录。
// Caretaker 管理者,负责存储和管理备忘录
type Caretaker struct {
mementos []*Memento // 存储所有历史备忘录
}
// NewCaretaker 创建新的管理者实例
func NewCaretaker() *Caretaker {
return &Caretaker{
mementos: make([]*Memento, 0),
}
}
// AddMemento 添加新的备忘录到历史记录
func (c *Caretaker) AddMemento(m *Memento) {
c.mementos = append(c.mementos, m)
}
// GetLastMemento 获取上一个版本的备忘录,用于撤销操作
func (c *Caretaker) GetLastMemento() *Memento {
if len(c.mementos) == 0 {
return nil
}
// 弹出最后一个备忘录
lastIndex := len(c.mementos) - 1
lastMemento := c.mementos[lastIndex]
c.mementos = c.mementos[:lastIndex]
return lastMemento
}
第四步:测试代码验证功能
模拟文本编辑的过程,多次修改内容并保存备忘录,最后执行撤销操作验证状态恢复效果。
package main
import "fmt"
func main() {
// 创建文本编辑器和管理者
editor := NewTextEditor()
caretaker := NewCaretaker()
// 第一次修改内容,保存备忘录
editor.WriteContent("第一版文本内容")
caretaker.AddMemento(editor.CreateMemento())
fmt.Printf("当前版本:%d,内容:%sn", editor.GetCurrentVersion(), editor.GetCurrentContent())
// 第二次修改内容,保存备忘录
editor.WriteContent("第二版文本内容,新增了部分说明")
caretaker.AddMemento(editor.CreateMemento())
fmt.Printf("当前版本:%d,内容:%sn", editor.GetCurrentVersion(), editor.GetCurrentContent())
// 第三次修改内容,保存备忘录
editor.WriteContent("第三版文本内容,调整了段落结构")
caretaker.AddMemento(editor.CreateMemento())
fmt.Printf("当前版本:%d,内容:%sn", editor.GetCurrentVersion(), editor.GetCurrentContent())
// 执行第一次撤销,恢复到第二版
lastMemento := caretaker.GetLastMemento()
if lastMemento != nil {
editor.RestoreFromMemento(lastMemento)
fmt.Printf("撤销后版本:%d,内容:%sn", editor.GetCurrentVersion(), editor.GetCurrentContent())
}
// 执行第二次撤销,恢复到第一版
lastMemento = caretaker.GetLastMemento()
if lastMemento != nil {
editor.RestoreFromMemento(lastMemento)
fmt.Printf("撤销后版本:%d,内容:%sn", editor.GetCurrentVersion(), editor.GetCurrentContent())
}
}
实现注意事项
- 备忘录的内部状态应该尽量只对原发器可见,避免其他对象直接修改备忘录的内容,破坏封装性。
- 如果原发器的状态数据量很大,频繁创建备忘录会占用较多内存,可以考虑只保存状态的变化量,或者设置备忘录的最大存储数量。
- 管理者的设计可以根据实际需求调整,比如支持跳转到指定版本、清空历史记录等功能,只需要扩展对应的方法即可。
适用场景
备忘录模式非常适合需要保存对象历史状态、支持撤销回滚的场景,比如编辑器的撤销重做、表单的修改记录回溯、游戏进度的保存加载等。在Golang中实现该模式时,只要理清三个核心角色的职责,按照上述示例的思路拆分逻辑,就可以快速实现对应的功能。