Go中备忘录模式需手动实现,核心是通过值拷贝或显式深拷贝创建不可变状态快照,用只读接口隔离访问,Originator通过完整替换实现原子恢复,禁用序列化与浅拷贝。
Go 语言没有类、继承或访问修饰符,无法直接复刻传统面向对象语义下的 Memento 模式(比如私有字段封装 + 窄接口暴露)。所谓“备忘录对象”,本质是**对某时刻状态的不可变快照 + 安全恢复能力**。关键不在名字,而在能否隔离状态、防止外部篡改、避免深拷贝开销失控。
最常用且符合 Go 风格的做法:定义一个只读接口描述备忘录,用结构体实现它,并让原对象(Originator)负责创建和恢复。所有字段必须导出(否则无法初始化),但通过接口隐藏写操作。
Originator 的状态字段应为值类型(如 int、string、struct{})或深度不可变引用类型(如 *sync.Map),避免浅拷贝导致恢复时意外共享
State() 或类似只读访问器(如果需要 inspect)Originator.Restore(memento Memento) 应完全替换内部状态,而非逐字段赋值——防止遗漏或顺序依赖type Memento interface {
// 空接口即可,或定义只读方法(如 State() map[string]interface{})
}
type originatorState struct {
data string
step int
}
type Originator struct {
state originatorState
}
func (o *Originator) Save() Memento {
// 值拷贝,安全
return &originatorState{data: o.state.data, step: o.state.step}
}
func (o *Originator) Restore(m Memento) {
if m == nil {
return
}
if s, ok := m.(*originatorState); ok {
o.state = *s // 完整替换,非部分更新
}
}
若 Originator 包含 []byte、map[string]int 或自定义结构体指针,Save() 中直接赋值会共享底层数据。恢复时修改备忘录会影响历史状态,彻底破坏模式语义。
[]T:用 append([]T(nil), src...) 或 copy(dst, src)
map[K]V:必须新建 map 并遍历复制键值对
time.Time 替 *time.Time)func (o *Originator) Save() Memento {
// 错误:共享 slice 底层数组
// return &originatorState{data: o.state.data, logs: o.state.logs}
// 正确:深拷贝 logs
logsCopy := make([]string, len(o.state.logs))
copy(logsCopy, o.state.logs)
return &originatorState{
data: o.state.data,
logs: logsCopy,
}
}
有人倾向用 json.Marshal 存状态再 json.Unmarshal 恢复,这属于序列化/反序列化,不是备忘录模式。它带来额外 CPU 开销、丢失类型信息(如 time.Time 变成字符串)、无法处理未导出字段或循环引用,且无法保证恢复后对象行为一致(比如方法接收者绑定)。