目录
1、核心思想
目的:在不破坏元对象封装性的前提下捕获其在某些时刻的内部状态,并像历史快照一样将它们保留在元对象之外,以备恢复之用。
举例:
1> 文本编辑器的撤销/重做功能
2> 数据库事务回滚
3> 浏览器前进后退功能
2、实现方式
2.1 模式结构
三种主要角色:
- Originator(元/原发器):需要保存状态的对象,负责创建备忘录和从备忘录恢复状态。比如编辑器中的文档对象
- Memento(备忘录):与元对象相仿,存储元对象内部状态的快照,仅允许元访问其内部数据,通常设计为不可变对象,一个状态对应一个备忘录对象。
- CareTaker(看护人/负责人):历史记录的维护者,持有所有记录的历史记录,并且提供对元数据对象的恢复操作,如撤销undo()、重做redo()等,一般不提供对历史记录的修改。
模式变体与优化:
- 增量备忘录:仅保存状态变化部分而非全量数据,减少内存占用
- 结合原型模式:通过克隆(Clone)快速生成备忘录,适用于复杂对象状态的保存
2.2 实现案例
文档撤销功能:
// 1、元:文档类
public class Doc {
private String title;//文档标题
private String body;//文档内容
public Doc(String title) {
this.title = title; //新建文档先命名
this.body = "";//新建文档内容为空
}
//省略set、get
public History createHistory() {
return new History(body);//创建历史记录
}
public void restoreHistory(History history) {
this.body = history.getBody();//恢复历史记录
}
}
// 2、备忘录:历史快照类
public class History {
private String body;//用于备忘文档内容
public History(String body) {
this.body = body;
}
public String getBody() {
return body;
}
}
// 3、看护人
public class Editor {
private Doc doc;
private Stack<History> historyRecords;//历史记录列表
public Editor(Doc doc) {
System.out.println("<<<打开文档" + doc.getTitle());
this.doc = doc;//载入文档
historyRecords = new Stack<>();//初始化历史记录列表
backup();//载入文档后保存第一份历史记录
show();//显示内容
}
public void append(String txt) {
System.out.println("<<<插入操作");
doc.setbody(doc.getBody() + txt);
backup();//添加后保存一份历史记录
show();
}
public void delete() {
System.out.println("<<<删除操作");
doc.setbody("");
backup();//删除后保存一份历史记录
show();
}
private void show() {
System.out.println(doc.getBody());
System.out.println("文档结束>>>\n");
}
//添加快照
private void backup() {
historyRecords.push(doc.createHistory());
}
//撤销操作
public void undo() {
System.out.println(">>>撤销操作");
History history = historyRecords.pop();
if(history != null) {
doc.setBody(history);//取出历史记录并恢复至文档
show();
}
}
}
// 4、客户端
public class Client {
public static void main(String[] args) {
Editor editor = new Editor(new Doc("《AI的觉醒》"));
editor.append("第一章 混沌初开");
editor.append("\n 正文部分");
editor.append("\n第二章 荒漠之花");
editor.delete();
editor.undo();
}
}
3、优缺点分析
优点:
-
封装性:不暴露对象内部状态,符合迪米特法则。
-
简化原发器:将状态管理职责分离到备忘录和负责人。
-
灵活撤销/重做:支持多次撤销(通过栈或列表管理历史状态)。
-
状态快照:可保存任意时刻的状态,用于审计或调试。
缺点:
-
内存消耗:频繁保存大对象状态可能导致内存占用高。(为备忘录加上容量限制,如总保存最近的20条记录)
-
性能开销:深拷贝复杂对象可能影响性能。
-
维护成本:需确保备忘录与原发器状态同步。
4、适用场景
-
撤销/重做功能
-
如文本编辑器、绘图软件、游戏操作回退。
-
-
事务回滚
-
数据库操作失败时恢复到之前的状态。
-
-
状态快照存档
-
游戏存档、系统配置备份。
-
-
协作编辑历史
-
如Google Docs的版本历史功能。
-