设计模式之备忘录模式
- 2019 年 12 月 30 日
- 筆記
定义
备忘录模式(Momento Pattern):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。又叫快照模式。
备忘录模式是一个比较简单的模式,用于备份对象状态。通常可以与命令模式等一起使用。
角色
备忘录模式有三个角色:备忘录(Memento)角色,发起人(Originator)角色,负责人(Caretaker)角色。

从类图上可以看出,发起人和备忘录角色都有 state 属性,其实可以把备忘录角色当作发起人角色的属性容器,发起人决定要备份哪些状态,并将状态放到备忘录中,备忘录存储这些状态,并且可以被保存起来,在需要的时候提供给发起人用于恢复到以前。
模式说明
我们结合命令模式来说明备忘录模式,依然使用浏览器的返回场景来说明。用户在浏览器中点击某个超链接跳到另一个页面,点击返回回到上一个链接页面,我们将点击抽象成命令,将返回抽象成恢复状态,浏览器就是我们的发起人角色。
/** * 在浏览器中是URL跳转 */ public interface Command<T> { void execute(T param); void back(); } @Data public class Memento { private String url; } // 浏览器窗口 public class Window { private String url; public Memento createMemento() { Memento m = new Memento(); m.setUrl(this.url); return m; } public void restoreMemento(Memento m) { rendUrlAndShow(m.getUrl()); } public void rendUrlAndShow(String url) { this.url = url; System.out.println(url); } } public class UrlCommand implements Command<String> { private Window window; private Memento m; public void execute(String url) { this.m = window.createMemento(); window.rendUrlAndShow(url); } public void back() { // 将原窗口再设置回去 window.restoreMemento(m); } } public class User { public static void main(String[] args) { // 在浏览器里用户点击 a.com String url = "www.a.com"; Command<String> commandA = new UrlCommand(); commandA.execute(url); // 用户点击返回 stack.pop().back(); } }
在这里 UrlCommand
既是 命令模式中的Command
角色也是备忘录模式中的 负责人
角色。
模式变种
- 由于备忘录角色只是一个属性容器,用于保存需要的属性,因此发起人自己就可以充当备忘录角色,这时可以使原型模式来创建当前对象的克隆,然后保存这个副本即可
- 备忘录保存的是发起对象的属性,这些属性可以直接保存到 key-value 的 map 中,无需单独创建一个类
- 备忘录模式只是描述单一备份的情况,在有多个备份的情况下可以配合
Stack
等数据结构使用,例如浏览器支持多次返回,例子如下:
public class User { public static void main(String[] args) { Stack<String> stack = new Stack<>(); // 在浏览器里用户点击 a.com String url = "www.a.com"; Command<String> commandA = new UrlCommand(); commandA.execute(url); stack.push(commandA); // 用户点击b.com url = "www.b.com" Command<String> commandB = new UrlCommand(); commandB.execute(url); stack.push(commandB); // 用户点击返回 stack.pop().back(); // 用户又点击一次返回 stack.pop().back(); } }
优点
- 在不破坏封装的前提下保存一个对象的状态
- 与其他模式共同使用,可以方便实现一些功能,如命令模式的 undo
- 备忘录模式是实现检查点(check point)的一种方法
缺点
- 在备份对象过多时,会占用很多内存,此时需要与外部存储交互