备忘录模式通过在不破坏封装性的前提下保存并恢复对象状态来实现撤销等功能,1. 发起人创建包含当前状态的备忘录对象并返回给管理者;2. 管理者负责存储备忘录但无法访问其内容;3. 恢复时管理者将备忘录交还发起人,由发起人从中提取状态并重置自身;4. 备忘录通常采用深拷贝防止引用类型共享问题;5. 该模式适用于文本编辑器撤销、游戏存档、配置版本切换等场景,但需注意内存消耗与性能优化。
备忘录模式(Memento Pattern)是一种行为型设计模式,它的核心作用是在不破坏封装性的前提下,保存对象的内部状态,并在之后恢复该状态。这种机制常用于实现撤销(Undo)、回滚、快照等功能。
一、备忘录模式如何保存对象状态
在备忘录模式中,对象状态的保存是通过一个中间对象——“备忘录”(Memento)来完成的。这个过程涉及三个关键角色:
- 发起人(Originator):需要保存状态的对象,它能创建一个包含当前状态的备忘录,也能通过备忘录恢复状态。
- 备忘录(Memento):负责存储发起人的内部状态,通常将状态设为私有,仅允许发起人访问。
- 管理者(Caretaker):负责保存和管理备忘录,但不能访问或修改备忘录内容。
状态保存流程如下:
- 发起人调用自身的
createMemento()
方法,将当前内部状态封装到一个新的备忘录对象中。
- 备忘录对象创建时,会复制发起人当前的所有关键状态字段。
- 发起人将备忘录返回给管理者,由管理者保存(例如存入列表、栈等)。
// 示例:Java 简化代码 class Document { private String content; private String fontName; private int fontSize; public Memento createMemento() { return new Memento(content, fontName, fontSize); } public void restore(Memento memento) { this.content = memento.getContent(); this.fontName = memento.getFontName(); this.fontSize = memento.getFontSize(); } // 内部类,仅对外暴露必要接口 public static class Memento { private final String content; private final String fontName; private final int fontSize; public Memento(String content, String fontName, int fontSize) { this.content = content; this.fontName = fontName; this.fontSize = fontSize; } // 仅提供 getter,不提供 setter public String getContent() { return content; } public String getFontName() { return fontName; } public int getFontSize() { return fontSize; } } }
这样,状态被完整复制到备忘录中,而外部无法篡改,保证了封装性。
二、对象状态恢复机制是如何工作的
状态恢复是备忘录模式的另一核心功能。当需要回退到某个历史状态时,发起人从管理者处获取对应的备忘录,并用其数据覆盖当前状态。
恢复流程:
- 管理者从存储中取出某个备忘录(比如最近一次的、或指定版本的)。
- 将该备忘录传回给发起人。
- 发起人调用
restore(memento)
方法,用备忘录中的值重置自己的内部状态。
class History { private final Stack<Document.Memento> mementos = new Stack<>(); public void push(Document.Memento memento) { mementos.push(memento); } public Document.Memento pop() { if (mementos.isEmpty()) return null; return mementos.pop(); } } // 使用示例 Document doc = new Document(); History history = new History(); doc.setContent("Hello"); doc.setFontSize(12); history.push(doc.createMemento()); // 保存状态 doc.setContent("World"); // 修改状态 System.out.println(doc.getContent()); // 输出: World Document.Memento m = history.pop(); if (m != null) { doc.restore(m); // 恢复 } System.out.println(doc.getContent()); // 输出: Hello
这样就实现了“撤销”操作。管理者控制版本存储结构(栈、列表、树等),可以支持多级撤销或版本选择。
三、关键设计要点与注意事项
- 封装性保护:备忘录的内部状态应为私有,只有发起人能读取,防止外部误改。
- 深拷贝 vs 浅拷贝:如果状态中包含引用类型(如集合、对象),需进行深拷贝,否则恢复时可能出错。
- 性能与内存:频繁保存状态会占用较多内存,尤其在大数据对象或高频操作场景下。可结合快照策略(如定时保存、差量保存)优化。
- 序列化替代方案:在某些语言中,可通过对象序列化+字节流存储实现类似功能,但牺牲了类型安全和性能。
四、典型应用场景
- 文本编辑器的撤销/重做功能
- 游戏中的存档与读档
- 数据库事务回滚的简化模型
- 配置管理中的版本切换
基本上就这些。备忘录模式通过“快照+隔离访问”的方式,实现了安全、可控的状态保存与恢复,是实现撤销机制的经典解法。虽然实现略显繁琐,但在需要精确控制状态流转的场景中非常实用。
评论(已关闭)
评论已关闭