备忘录模式核心结构需三个角色:Memento(只读快照)、Originator(创建/恢复状态)、Caretaker(保管但不访问快照);Memento须私有、只读,Originator通过CreateMemento和SetMemento操作,Caretaker仅管理栈。
备忘录模式的核心结构怎么搭
关键不是“存状态”,而是隔离状态存储与业务逻辑。必须有三个角色:Memento(只读快照)、Originator(被备份的对象,能创建和恢复自身状态)、Caretaker(保管 Memento,但不能访问其内容)。C# 里最容易错的是把 Memento 设成 public 字段或属性——这会破坏封装,让外部随意修改快照。
实操建议:
-
Memento类必须是private嵌套类或internal类,构造函数接收Originator的内部状态,仅提供只读属性(如public string State { get; }) -
Originator提供两个方法:CreateMemento()返回新快照,SetMemento(Memento m)从快照恢复;注意不要在SetMemento中直接赋值字段,而应调用私有恢复逻辑 -
Caretaker只持有Stack或List,不碰Memento内部字段
撤销/恢复操作怎么支持多步和重做
单步 Undo 很简单,但真实场景需要历史栈 + 重做栈。如果只用一个 Stack,执行 Undo 后再修改对象,旧的“重做点”就丢了。
实操建议:
- 维护两个栈:
private readonly Stack _undoStack = new();和private readonly Stack _redoStack = new(); - 每次修改前调用
_undoStack.Push(originator.CreateMemento()),同时清空_redoStack(因为新操作使重做失效) -
Undo()弹出_undoStack顶部,恢复后把原快照压入_redoStack;Redo()则反向操作 - 注意:如果对象很大,频繁深拷贝状态会吃内存,可考虑用差分快照或只存变更字段(需额外元数据支持)
如何避免序列化导致的性能和兼容性问题
有人直接用 JsonSerializer.Serialize(originator) 存整个对象——这看似省事,但实际埋雷:版本升级后字段名改了、新增了不可序列化成员、或者 DateTime 时区处理不一致,都会让旧快照无法恢复。