一、Memento模式的目的:
memento是一个保存另外一个对象内部状态拷贝的对象.这样以后就可以将该对象恢复到原先保存的状态.
二、Memento模式的简单例子:
下面的例子来源于Jdon网站的《GoF设计模式系列》的《Memento模式》,虽然简单却形象地表明了Memento模式的应用
public class Originator { private int number; private File file = null; public Originator(){} // 创建一个Memento,将自身作为参数传入 public Memento getMemento(){ return new Memento(this); } // 从Memento中取出保存的数据,恢复为原始状态 public void setMemento(Memento m){ number = m.number; file = m.file; } } private class Memento implements java.io.Serializable{ private int number; private File file = null; public Memento( Originator o){ number = o.number; file = o.file; } }
三、Memento模式的特点:
在需要提供保存、恢复对象状态的类中,必须提供两个方法:
.保存对象当前状态方法:将对象自身(this)作为参数传入,创建备忘录。
.恢复对象之前状态的方法:取出备忘录/接收一个备忘录对象,从中获取对象之前的状态
模式的缺点是耗费大,如果内部状态很多,再保存一份,无意要浪费大量内存.
注意:Memento模式保存的是操作前对象的状态,而不是操作后对象的状态;否则就没办法做恢复了
四、去其他模式关系
1、“Mementor”模式和“堆栈”的结合-“GUI界面撤销功能”的实现
·当用户在面板中拖动一个组件到编辑区时,应用程序为编辑区创建一个备忘录,并把它加入到一个堆栈中(注意此时备忘录中包含的是操作前的状态,而非操作后的状态)
·当用户单击“撤销”按钮时,应用程序就将堆栈顶部的备忘录弹出,然后将编辑区恢复为该备忘录所记录的状态
当可视化应用程序启动后,首先向空的堆栈中压入一个初始的空备忘录,并且保证绝对不会将该备忘录从栈中弹出,从而确保该栈的顶部总是有一个有效的备忘录。当栈中仅包含一个备忘录的时候,应用程序应当禁用“撤销”按钮
2、“Mementor”模式和“Observer”模式的结合-“通知式恢复”
在某些情况下,我们希望为GUI组件注册一些监听器,当组件的状态发生改变时,可以通知所有对它感兴趣的监听器,这种情况我们可以用“Observer”模式来实现。
还是以我们上面的例子来说:假如用户从面板中拖动一个组件到编辑区后,有几个监听器对它感兴趣,并且采取了相应的操作,现在用户单击了“撤销”按钮,那么我们应该把这个事件通知所有监听器,告诉他们必须恢复之前所有的状态。这种情况就可以把两种模式结合起来使用:
·当对象被创建时,激活并为该对象注册监听器(观察着),监听器创建一个初始化备忘录,保存编辑区的原始信息
·当对象(被观察着)被销毁(用户单击“撤销”按钮时),向所有注册的监听器发送信息
·监听器(观察者)接收到信息,从备忘录中取出编辑区信息的备忘录,恢复当前编辑区的状态
3、“Mementor”模式和“Observer”模式、“责任链”模式的结合-“链式通知恢复”
在上面我们提到了将“Mementor”模式和“Observer”模式结合起来达到到“通知式恢复”的效果,考虑下面一个情况:
如果我们在安装一个软件或执行一个长时间、多次交互的情况,加入用户在最后一个操作中选择了“取消”操作,我们应该怎么做呢?
我的想法是在为每一次操作创建一个备忘录,并将其放在“责任链”上,当最后用户选择取消时,沿着这条“责任链”一个个通知观察者,由观察者取出备忘录,执行恢复工作。
注意:这个方法和第二个方法有点区别:第二个方法不管恢复的顺序,而第三种方法适合讲究恢复顺序的情况,例如前面提到的软件安装的撤销。
4、“Mementor”模式和“Flyweight”模式的结合:减少相同对象的拷贝
用于保存对象状态的“备忘录”对象,必须拥有一份和被保存对象相同的属性拷贝。
对于“备忘录”有可能导致内存消耗过大的情况,如果对象中的属性是“公用的”(即多个对象可以共享一个属性),那么我们可以考虑采用“享元模式”,减少相同属性对象的创建。但是这个方法对于属性多为运行时确定的情况作用不大。