“这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战”
一、什么是备忘录模式?
备忘录模式(Memento Pattern)又称为快照模式(Snapshot Pattern)或令牌模式(Token Pattern),是指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态,属于行为型模式。
在软件系统中,备忘录模式可以为我们提供一种 “后悔药” 的机制,它通过存储系统各个历史状态的快照,使得我们可以在任一时刻将系统回滚到某一个历史状态。
二、备忘录模式的应用场景
对于我们程序员来说,可能天天都在用备忘录模式,比如我们每天使用的Git、SVN都可以提供一种代码版本撤回的功能。还有一个比较贴切的现实场景应该是游戏的存档功能,通过将游戏当前进度存储到本地文件系统或数据库中,使得下次继续游戏时,玩家可以从之前的位置继续进行。
备忘录模式适用于以下应用场景
- 需要保存历史快照的场景。
- 希望在对象之外保存状态,且除了自己其他类对象无法访问状态保存的具体内容。
三、备忘录模式涉及的角色
首先来看下备忘录模式的通用UML类图:
从UML类图中,我们可以看到,备忘录模式主要包含三种角色:
- 发起人角色(Originator):负责创建一个备忘录,记录自身需要保存的状态,具备状态回滚功能。
- 备忘录角色(Memento):用于存储Originator的内部状态,且可以防止Originator以外的对象进行访问。
- 备忘录管理员角色(Caretaker):负责存储,提供管理备忘录(Memento),无法对备忘录内容进行操作和访问。
四、利用压栈管理落地备忘录模式
我们肯定都用过网页中的富文本编辑器,编辑器中的通常会附带草稿箱、撤销等这样的操作。下面我们用一段代码来实现一个这样的功能。假设,我们发布一篇文章,文章编辑的过程需要花很长时间,中间也会不停地撤销、修改。甚至可能要花好几天才能写出一篇精品文章,因此可能会将已经编辑好的内容实时保存到草稿箱。
首先创建发起人角色编辑器Editor类:
@Data
@AllArgsConstructor
public class Editor {
private String title;
private String content;
private String images;
public ArticleMemento saveToMemento() {
ArticleMemento articleMemento = new ArticleMemento(this.title, this.content, this.images);
return articleMemento;
}
public void undoFromMemento(ArticleMemento articleMemento) {
this.title = articleMemento.getTitle();
this.content = articleMemento.getContent();
this.images = articleMemento.getImages();
}
}
然后创建备忘录角色ArticleMemento类:
@Data
@AllArgsConstructor
public class ArticleMemento {
private String title;
private String content;
private String images;
}
最后创建备忘录管理角色草稿箱DraftsBox类:
public class DraftsBox {
private final Stack<ArticleMemento> stack = new Stack<>();
public ArticleMemento getMemento() {
ArticleMemento articleMemento = stack.pop();
return articleMemento;
}
public void addMemento(ArticleMemento articleMemento) {
stack.push(articleMemento);
}
}
编写客户端测试代码:
public class Test {
public static void main(String[] args) {
DraftsBox draftsBox = new DraftsBox();
Editor editor = new Editor("设计模式学习之旅", "好好学习天天向上", "666888.png");
ArticleMemento articleMemento = editor.saveToMemento();
draftsBox.addMemento(articleMemento);
System.out.println("标题:" + editor.getTitle() + "\n" + "内容:" + editor.getContent() + "\n" + "插图:" + editor.getImages());
System.out.println("完成的信息:" + editor);
System.out.println("====首次修改文章====");
editor.setTitle("备忘录模式");
editor.setContent("Mark在学习此模式中。。。");
System.out.println("====首次修改文章完成====");
System.out.println("完成的信息:" + editor);
articleMemento = editor.saveToMemento();
draftsBox.addMemento(articleMemento);
System.out.println("====保存到草稿箱====");
System.out.println("====第二次修改文章====");
editor.setTitle("单例模式");
editor.setContent("Zoe在学习此模式中。。。");
System.out.println("完成的信息:" + editor);
System.out.println("====第二次修改文章完成====");
System.out.println("====第一次撤销====");
articleMemento = draftsBox.getMemento();
editor.undoFromMemento(articleMemento);
System.out.println("完成的信息:" + editor);
System.out.println("====第一次撤销完成====");
System.out.println("====第二次撤销====");
articleMemento = draftsBox.getMemento();
editor.undoFromMemento(articleMemento);
System.out.println("完成的信息:" + editor);
System.out.println("====第二次撤销完成====");
}
}
运行结果如下:
五、备忘录模式的优缺点
优点:
- 简化发起人实体类(Originator)职责,隔离状态存储与获取,实现了信息的封装,客户端无需关心状态的保存细节。
- 提供状态回滚功能。
缺点:
- 消耗资源:如果需要保存的状态过多时,每一次保存都会消耗很多内存。
六、友情链接
欢迎大家关注微信公众号(MarkZoe)互相学习、互相交流。