备忘录模式
备忘录模式即快照模式(Snapshot Pattern)或Token模式是对象的行为模式。
备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。
备忘录模式的结构图如下所示

备忘录模式的角色有三个:备忘录(Memento)角色、发起人(Originator)角色、负责人(Caretaker)角色
备忘录角色的责任:
(1)将发起人(Originator)对象的内部状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。
(2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
备忘录有两个等效的接口:
窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
发起人角色有如下责任:
(1)创建一个含有当前的内部状态的备忘录对象。
(2)使用备忘录对象存储其内部状态。
负责人角色有如下责任:
(1)负责保存备忘录对象。
(2)不检查备忘录对象的内容。
白箱”备忘录模式的实现
备忘录角色对任何对象提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开,叫做“白箱实现”。
“白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。

发起人角色类,发起人角色利用一个新创建的备忘录对象将自己的内部状态存储起来
public class Originator { private String state; public Memento createMemento() { return new Memento(state); } public void restoreMemento(Memento memento) { this.state = memento.getState(); } public String getState() { return state; } public void setState(String state) { this.state = state; } }
备忘录角色类,备忘录对象将发起人对象传入的状态存储起来
public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } }
负责人角色类,负责人角色负责保存备忘录对象,但是从不修改(甚至不查看)备忘录对象的内容
public class Caretaker { private Memento memento; /** * 备忘录取值方法 * * @return */ public Memento retrieveMemento() { return memento; } /** * 备忘录赋值方法 * * @param memento */ public void saveMemento(Memento memento) { this.memento = memento; } }
客户端角色类
@Slf4j public class Client { public static void main(String[] args) { Originator originator = new Originator(); Caretaker caretaker = new Caretaker(); originator.setState("on"); caretaker.saveMemento(originator.createMemento()); originator.setState("off"); originator.restoreMemento(caretaker.retrieveMemento()); log.info("{}", originator.getState()); } }
首先将发起人对象的状态设置成“On”,并创建一个备忘录对象将这个状态存储起来;然后将发起人对象的状态改成“Off”;最后又将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态
下图是将发起人对象的状态存储到白箱备忘录对象中去的时序图

将发起人对象恢复到备忘录对象所记录的状态的时序图如下所示

“黑箱”备忘录模式的实现
备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。
将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口MementoIF给Caretaker以及其他对象。这样,Originator类看到的是Menmento的所有接口,而Caretaker以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口

发起人角色类Originator中定义了一个内部的Memento类。由于此Memento类的全部接口都是私有的,因此只有它自己和发起人类可以调用
package com.design.mode.memento.blank; public class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } public MementoIF createMemento() { return new Memento(state); } public void restoreMemento(MementoIF memento) { setState(((Memento) memento).getState()); } private class Memento implements MementoIF { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } } }
窄接口MementoIF,这是一个标识接口
public interface MementoIF { }
负责人角色类Caretaker能够得到的备忘录对象是以MementoIF为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容
public class Caretaker { private MementoIF memento; public MementoIF retrieveMemento() { return memento; } public void saveMemento(MementoIF memento) { this.memento = memento; } }
客户端角色类
@Slf4j public class Client { public static void main(String[] args) { Originator originator = new Originator(); Caretaker caretaker = new Caretaker(); originator.setState("on"); caretaker.saveMemento(originator.createMemento()); originator.setState("off"); originator.restoreMemento(caretaker.retrieveMemento()); log.info("{}", originator.getState()); } }
“自述历史”模式
在“自述历史”模式里面,发起人角色自己兼任负责人角色。

发起人角色同时还兼任负责人角色,也就是说它自己负责保持自己的备忘录对象。
public class Originator { public String state; public void changeState(String state) { this.state = state; } public Memento createMemento() { return new Memento(this); } public void restoreMemento(MementoIF memento) { Memento m = (Memento) memento; changeState(m.state); } private class Memento implements MementoIF { private String state; public Memento(Originator originator) { this.state = originator.state; } public String getState() { return state; } } }
客户端角色类
@Slf4j public class Client { public static void main(String[] args) { Originator originator = new Originator(); originator.changeState("state 0"); MementoIF memento = originator.createMemento(); originator.changeState("state 1"); originator.restoreMemento(memento); log.info("{}", originator.state); } }
浙公网安备 33010602011771号