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