简介
备忘录模式(Memento Pattern)又叫作快照模式(Snapshot Pattern)或令牌模式(Token Pattern),指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态,属于行为型设计模式。
通用模板
-
创建备忘录角色:用于存储Originator(发起人角色)的内部状态,且可以防止Originator以外的对象进行访问。
// 备忘录 public class Memento {private String state;public Memento(String state) {this.state = state;}public String getState() {return this.state;} }
-
创建发起人角色:负责创建一个备忘录,记录自身需要保存的状态;具备状态回滚功能。
// 发起人 public class Originator {// 内部状态private String state;public String getState() {return this.state;}public void setState(String state) {this.state = state;}// 创建一个备忘录public Memento createMemento() {return new Memento(this.state);}// 从备忘录恢复public void restoreMemento(Memento memento) {this.state = memento.getState();} }
-
创建备忘录管理者角色:负责存储、提供管理Memento(备忘录),无法对Memento的内容进行操作和访问。
// 备忘录管理者 public class Caretaker {// 备忘录对象private Memento memento;public Memento getMemento() {return this.memento;}public void storeMemento(Memento memento) {this.memento = memento;} }
模板测试
-
测试代码
public class Client {public static void main(String[] args) {// 创建一个发起人角色Originator originator = new Originator();// 创建一个备忘录管理员角色Caretaker caretaker = new Caretaker();// 设置初始状态originator.setState("状态A");System.out.println("初始状态:" + originator.getState());// 保存当前状态caretaker.storeMemento(originator.createMemento());// 更改状态originator.setState("状态B");System.out.println("更改后的状态:" + originator.getState());// 恢复到之前的状态originator.restoreMemento(caretaker.getMemento());System.out.println("恢复后的状态:" + originator.getState());} }
-
测试结果
初始状态:状态A 更改后的状态:状态B 恢复后的状态:状态A
应用场景
对于程序员来说,可能天天都在使用备忘录模式,比如我们每天使用的Git、SVN都可以提供一种代码版本撤回的功能。还有一个比较贴切的现实场景就是游戏的存档功能,通过将游戏当前进度存储到本地文件系统或数据库中,使得下次继续游戏时,玩家可以从之前的位置继续进行。
备忘录模式主要适用于以下应用场景。
(1)需要保存历史快照的场景。
(2)希望在对象之外保存状态,且除了自己,其他类对象无法访问状态保存的具体内容。
优点
(1)简化发起人实体类(Originator)的职责,隔离状态存储与获取,实现了信息的封装,客户端无须关心状态的保存细节。
(2)提供状态回滚功能。
缺点
备忘录模式的缺点主要是消耗资源。如果需要保存的状态过多,则每一次保存都会消耗很多内存。
“生搬硬套”实战
场景描述
假设你在编写一个文本编辑器应用程序,用户在编辑文档时,希望能够随时保存当前编辑的状态,并在需要时恢复到之前保存的状态。这就需要用到备忘录模式来实现撤销功能。
代码开发
-
创建备忘录角色(这里指用来存储文档的状态的备忘录)
// 定义一个备忘录类来存储文档的状态 public class DocumentMemento {private String content;public DocumentMemento(String content) {this.content = content;}public String getContent() {return content;} }
-
创建发起人角色(这里指文档编辑器)
// 发起人角色(Originator),也就是我们的文档编辑器,它负责创建备忘录并恢复状态 public class DocumentEditor {private String content;public void setContent(String content) {this.content = content;}public String getContent() {return content;}// 创建备忘录public DocumentMemento createMemento() {return new DocumentMemento(content);}// 从备忘录恢复状态public void restoreFromMemento(DocumentMemento memento) {this.content = memento.getContent();} }
-
创建备忘录管理者角色(这里指存储文档备忘录的管理类)
import java.util.ArrayList; import java.util.List;// 管理者角色(Caretaker),它用来存储备忘录对象 public class DocumentCaretaker {private List<DocumentMemento> mementos = new ArrayList<>();public void addMemento(DocumentMemento memento) {mementos.add(memento);}public DocumentMemento getMemento(int index) {return mementos.get(index);} }
至此,我们就通过“生搬硬套”备忘录模式的模板设计出一套文档备忘录的案例,接下来我们进行测试:
-
测试代码
public class Test {public static void main(String[] args) {// 创建一个发起人角色DocumentEditor editor = new DocumentEditor();// 创建一个备忘录管理员角色DocumentCaretaker caretaker = new DocumentCaretaker();// 设置初始文档内容editor.setContent("Hello, ");System.out.println("初始文档内容: " + editor.getContent());// 保存当前状态caretaker.addMemento(editor.createMemento());// 更改文档内容editor.setContent(editor.getContent() + "World!");System.out.println("更改后文档内容: " + editor.getContent());// 恢复到之前的状态editor.restoreFromMemento(caretaker.getMemento(0));System.out.println("恢复后的文档内容: " + editor.getContent());} }
-
测试结果
初始文档内容: Hello, 更改后文档内容: Hello, World! 恢复后的文档内容: Hello,
总结
备忘录模式的本质是从发起人实体类(Originator)隔离存储功能,降低实体类的职责。同时由于存储信息(Memento)独立,且存储信息的实体交由管理类(Caretaker)管理,则可以通过为管理类扩展额外的功能对存储信息进行扩展操作(比如增加历史快照功能)。