备忘录模式属于行为型设计模式,其核心目标是让对象可以回溯到之前的状态,同时不会暴露对象的内部实现细节。在Golang中实现该模式,需要合理设计发起人、备忘录、管理者三个核心角色,通过接口和结构体组合完成状态保存与恢复的逻辑。

备忘录模式核心角色说明
备忘录模式通常包含三个核心角色,每个角色的职责明确,共同协作完成状态管理:
- 发起人(Originator):需要保存状态的对象,负责创建备忘录存储当前内部状态,也可以通过备忘录恢复自身状态。
- 备忘录(Memento):存储发起人的内部状态,通常提供获取和设置状态的方法,且对外部其他对象隐藏发起人的状态细节。
- 管理者(Caretaker):负责保存备忘录对象,但是不能对备忘录的内容进行操作或者检查,仅做存储管理。
Golang实现备忘录模式保存对象状态示例
下面以一个简单的文本编辑器场景为例,实现备忘录模式保存编辑器的文本内容状态,支持撤销操作。
1. 定义备忘录结构体
备忘录结构体只存储发起人的状态,不对外暴露状态修改方法,仅提供状态获取接口。
package main
// Memento 备忘录结构体,存储编辑器状态
type Memento struct {
content string // 存储的文本内容
}
// GetContent 获取备忘录中保存的内容
func (m *Memento) GetContent() string {
return m.content
}
2. 定义发起人结构体
发起人是文本编辑器本身,负责创建备忘录保存当前状态,也可以从备忘录中恢复状态。
// Editor 发起人,文本编辑器
type Editor struct {
content string // 编辑器的当前文本内容
}
// SetContent 设置编辑器内容
func (e *Editor) SetContent(content string) {
e.content = content
}
// GetContent 获取编辑器当前内容
func (e *Editor) GetContent() string {
return e.content
}
// CreateMemento 创建备忘录,保存当前状态
func (e *Editor) CreateMemento() *Memento {
return &Memento{
content: e.content,
}
}
// RestoreFromMemento 从备忘录恢复状态
func (e *Editor) RestoreFromMemento(m *Memento) {
e.content = m.GetContent()
}
3. 定义管理者结构体
管理者负责存储多个备忘录,支持获取指定位置的备忘录,实现多步撤销功能。
// Caretaker 管理者,管理备忘录
type Caretaker struct {
mementos []*Memento // 存储多个备忘录
}
// AddMemento 添加备忘录
func (c *Caretaker) AddMemento(m *Memento) {
c.mementos = append(c.mementos, m)
}
// GetMemento 获取指定索引的备忘录
func (c *Caretaker) GetMemento(index int) *Memento {
if index < 0 || index >= len(c.mementos) {
return nil
}
return c.mementos[index]
}
// GetLastMemento 获取最后一个备忘录,用于撤销操作
func (c *Caretaker) GetLastMemento() *Memento {
if len(c.mementos) == 0 {
return nil
}
return c.mementos[len(c.mementos)-1]
}
// RemoveLastMemento 移除最后一个备忘录
func (c *Caretaker) RemoveLastMemento() {
if len(c.mementos) == 0 {
return
}
c.mementos = c.mementos[:len(c.mementos)-1]
}
4. 完整调用示例
以下代码演示了如何使用上述实现完成状态保存和撤销操作:
package main
import "fmt"
func main() {
// 创建编辑器和备忘录管理者
editor := &Editor{}
caretaker := &Caretaker{}
// 第一次编辑内容,保存状态
editor.SetContent("第一版内容")
caretaker.AddMemento(editor.CreateMemento())
fmt.Println("当前内容:", editor.GetContent())
// 第二次编辑内容,保存状态
editor.SetContent("第二版内容")
caretaker.AddMemento(editor.CreateMemento())
fmt.Println("当前内容:", editor.GetContent())
// 第三次编辑内容,保存状态
editor.SetContent("第三版内容")
caretaker.AddMemento(editor.CreateMemento())
fmt.Println("当前内容:", editor.GetContent())
// 执行撤销操作,恢复到上一版状态
lastMemento := caretaker.GetLastMemento()
if lastMemento != nil {
editor.RestoreFromMemento(lastMemento)
caretaker.RemoveLastMemento()
}
fmt.Println("撤销后内容:", editor.GetContent())
// 再次撤销,恢复到第一版状态
lastMemento = caretaker.GetLastMemento()
if lastMemento != nil {
editor.RestoreFromMemento(lastMemento)
caretaker.RemoveLastMemento()
}
fmt.Println("再次撤销后内容:", editor.GetContent())
}
备忘录模式使用注意事项
在Golang中使用备忘录模式时,需要注意以下几点:
- 备忘录存储的状态如果过大,会占用较多内存,管理者存储多个备忘录时需要做好内存管理,避免内存溢出。
- 如果发起人状态较多,备忘录可以只存储变化的部分状态,而不是全量状态,减少存储开销。
- Go语言中没有严格的访问控制符,需要通过命名规范(比如小写字段)来约束备忘录的状态不被外部随意修改,保证封装性。
适用场景
备忘录模式适合以下场景使用:
- 需要保存对象历史状态,并且支持状态回滚的场景,比如编辑器的撤销重做、游戏的存档读档。
- 需要保存对象状态快照,并且不希望暴露对象内部细节的场景。
- 对象状态变化频繁,需要临时保存状态用于后续恢复的场景。