备忘录模式可以实现撤销机制。
问题提出
我们设计一个编辑器Editor,希望编辑的内容content可以多次撤销。
如图,我们创建了一个新package名为memento,并创建Editor类。Editor有private属性content,同时设置了getContent和setContent。再在main函数中,调用Editor,编辑其content,并允许调用undo()来撤销编辑的内容。
思考下,我们该如何实现?
解决方案
有一个很容易想到的方案,创建一个列表保存各个时间点的content,撤销时就从列表里读出保存的内容覆盖。
这个方案是合理的,但有一个问题,当Editor添加其他属性比如title时,我们必须再添加一个prevTitles列表来保存title的历史记录,可扩展性不好。
可以考虑增加一个EditorState类,用于保存Editor某个时间点的状态,而Editor类则拥有这些状态的列表prevStates。这样,Editor如果添加新属性,也仅需在EditorState添加相应属性即可。
这种设计违反了面向对象编程中”每个类只负责单一职责“的原则。Editor目前有两个职责,除了编辑内容外,还负责维持历史状态的工作,我们应该把它分出来。
我们添加History类用来保存状态历史,History拥有EditorState的列表,组成关系也从Editor转换到了History。再添加两个方法push和pop用于保存状态和恢复状态。
Editor添加两个方法createState和restore,当需要将状态保存时,调用createState方法返回EditorState对象,恢复时则调用restore方法。显然,Editor对EditorState有依赖关系。
通过分析,我们就逐步想出了这个模式,这个模式被称为备忘录模式。
查看原书,我们发现“四人帮”的命名与我们是不一样的,Editor叫Originator,EditorState叫Memento,History叫Caretaker,我们没必要改名,在实际项目中更应该使用有意义的命名,而不一定就要用“四人帮”的命名。
代码实现
添加EditorState类,该类只有一个属性content,我们在构造函数中赋值,并设置该属性为final,可防止意外修改增加程序的健壮性。Editor添加createState和restore方法。
添加History类,该类使用泛型List保存EditorState的列表,并增加push和pop方法,从技术上讲,这里更应该使用栈Stack,但为了不转移关注点,我们保持用List。
完成了备忘录模式(memento pattern)的创建,我们在main函数中测试一下。我们分别设置Editor内容为“a”、“b“、”c“,然后进行撤销回退操作,查看结果变回”b“,再回退则变回”a“,达到了预期效果。
小结
本文我们从一个编辑器回退操作的需求开始,逐步分析形成了备忘录模式,尝试理解了”四人帮“想出该模式的过程。
下一篇介绍状态模式。