一、介绍
备忘录模式(Memento Pattern),属于行为型设计模式。目的是用于保存一个对象在某一时刻的状态,以便于在将来某个时刻根据此状态恢复该对象。
在我们日常生活中,备忘录的使用十分频繁。比如,有一个事情我们完成了35%的进度,这时有另一件更重要的事情需要处理,那么我们需要对这个已完成35%的事情进行记录(做了哪些事情,做到什么程度等),当那个更重要的事情完成后,就可以根据记录的内容(做了哪些事情,做到什么程度等)继续处理这件事情,而不至于因为忘记处理进度而导致重复或疏漏,这个记录的过程就是备忘。
另一个十分鲜活的例子就是,我们经常在对一些东西进行计数时被其他人说话而打断,当你回答对方后自己却突然忘记数到哪里了,如果你在说话前已经数了相当长一段时间,那此时你可能不得不从0重新开始计数。当应用备忘录时,无论你的计数达到什么程度,在别人和你说话时,你先将当前计数保存记录下来,在谈话完成后,再继续计数即可,以此避免从0重新开始计数。
当我们系统中存在一个对象,
下面是备忘录模式的概念解释:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
二、应用举例
根据我们在介绍中的描述,我们应该明白了备忘录模式的本质其实就是对象状态的保存和读取。不难发现,备忘录模式的应用到处可见
-
几乎所有的软件
在我们使用的所有软件无论是桌面端还是网页端,只要有保存功能,就都是备忘录模式的体现。
比如,我们使用txt文本文档编辑文本,在编辑的过程中任意时刻我们都可以使用
ctrl+s
对其保存,当再次打开该txt文档时,展示的就是我们最后一次保存时的文本,这种备忘的介质就是硬盘。再比如,我们在某一个网站中对个人信息进行编辑时,当按下保存按钮时,网页会发起网络请求,将我们编辑的个人信息发送到远程服务器,当再次打开个人信息页面进行查看或编辑时,网页会再次向远程服务器发起请求获取我们上一次保存个人信息。这种备忘的介质就是远程数据库。
-
java序列化
java的序列化和反序列化过程也是备忘录模式的体现。其中,序列化的过程就相当于对一个对象状态的保存,反序列化的过程就相当于对以保存的对象状态进行恢复。
三、基本角色
在标准的备忘录模式中,包含以下三个角色:
-
Memento
保存对象的内部状态。即备忘录本身。而既然是备忘录,自然有两个最基本的方法来保存和获取其内部保存的状态:
getState()
和setState()
。存储源发器对象的状态。备忘录对象可以包括一个或多个状态属性,源发器可以根据需要保存和恢复状态。
-
Originator
创建备忘录,并保存指定对象的状态。即备忘录管理者。作为备忘录的管理者,自然在其内部保存着多个备忘(
mementoList
)和对应的添加删除方法(add(memento)
和remove(memento)
)。需要保存和恢复状态的对象。它创建一个备忘录对象,用于存储当前对象的状态,也可以使用备忘录对象恢复自身的状态。
-
Caretaker
从备忘录中读取对象的状态并恢复对象。有了备忘录管理者,我们对备忘录的操作应转移到管理者上,通过备忘录管理者存取备忘录中的状态。
负责保存备忘录对象,但不能修改备忘录对象的内容。它可以存储多个备忘录对象,并决定何时将备忘录恢复给源发器。
注意:一般情况下,备忘录模式无需抽象组件,但究竟需不需要抽象类根据实际情况来定。
备忘录的通用UML图如下所示
四、代码演示
下面我们通过代码对上面基本角色的分析进行演示
-
备忘录类
Memento
public class Memento { // 对象某一时刻的状态 private String state; // 获取对象的状态 public String getState() { return state; } // 保存对象的状态 public void setState(String state) { this.state = state; } }
-
备忘录管理员类
Originator
public class Originator { // 备忘录集合 private final List<Memento> mementoList; public Originator() { // 对备忘录集合进行初始化 this.mementoList = new ArrayList<>(); } // 保存对象某一状态到备忘录 public void saveState(String state) { Memento memento = new Memento(); memento.setState(state); mementoList.add(memento); } // 获取某一备忘录并将其移除 public Memento getMemento(int index) { return mementoList.remove(index); } }
-
读取状态类
Caretaker
public class Caretaker { // 备忘录管理者 private final Originator originator; // 备忘录管理者的初始化 public Caretaker(Originator originator) { this.originator = originator; } // 获取某个备忘录中对象的状态 public String getState(Integer index) { return originator.getMemento(index).getState(); } }
-
客户端测试代码
public class MementoDemo { public static void main(String[] args) { // 将对象的状态交给备忘录管理员通过备忘录管理 Originator originator = new Originator(); originator.saveState("第一个字符串"); originator.saveState("第二个字符串"); originator.saveState("第三个字符串"); originator.saveState("第四个字符串"); originator.saveState("第五个字符串"); // 读取备忘录中对象的状态 Caretaker caretaker = new Caretaker(originator); String state5 = caretaker.getState(4); System.out.println(state5); String state4 = caretaker.getState(3); System.out.println(state4); String state3 = caretaker.getState(2); System.out.println(state3); String state2 = caretaker.getState(1); System.out.println(state2); String state1 = caretaker.getState(0); System.out.println(state1); } }
运行以上代码,得到以下输出
五、总结
备忘录模式给我们提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态,在此同时,备忘录模式也实现了信息的封装,使得用户不需要关心状态的保存细节。
但是,备忘录模式的可扩展性并不是很好,每一种对象都需要对应一种备忘录,这将会导致类的数量膨胀,对内存无疑是一种考验。
纸上得来终觉浅,绝知此事要躬行。
————————我是万万岁,我们下期再见————————