其目的是,在不违反封装原则的前提下.采集和备份一个对象的内部状态以便这个对象可以在以后恢复到之前的某个状态.
在Memento模式中,有如下角色:
1.Memento (备忘录)
* 存储Originator的内部状态. 根据Originator的需要.可以存储任意的内部状态.
* 保护对状态的访问.除了Originator对象外,其他对象不能访问其存储的内部状态.Memeoto实际上提供了2个接口.
Caretaker只能访问Memento的窄接口(narrow interface) - 它只能够把memento传递给其他对象.
相反,Originator可以访问Memento的宽接口(wide interface). 通过这个接口,可以让Originator获取足够的信息以恢复到之前的状态.理想情况下,只有创建memento的Originator才有权限访问memento的内部状态信息.
2.Originator (发起人)
* 可以创建一个新的memento, 并把自己的当前状态信息存储到memento里面
* 可以使用memento来恢复其内部状态信息
3.Caretaker (负责人)
* 负责维护和管理memento对象
* 从不对memento的内容进行操作或者测试
备忘录模式的操作过程
1、客户端为发起人角色创建一个备忘录对象。
2、调用发起人对象的某个操作,这个操作是可以撤销的。
3、检查发起人对象所出状态的有效性。检查的方式可以是发起人对象的内部自查,也可以由某个外部对象进行检查。
4、如果需要的话,将发起人的操作撤销,也就是说根据备忘录对象的记录,将发起人对象的状态恢复过来。
“假如”协议模式的操作过程:
1、将发起人对象做一个拷贝。
2、在拷贝上执行某个操作。
3、检查这个拷贝的状态是否有效和自恰。
4、如果检查结果是无效或者不自恰的,那么扔掉这个拷贝,并触发异常处理程序;相反,如果检查是有效和自恰的,那么在原对象上执行这个操作
显然这一做法对于撤销一个操作并恢复操作前状态较为复杂和困难的发起人对象来说是一个较为谨慎和有效的做法。
“假如”协议模式的优点和缺点
具体来说,这个做法的长处是可以保证发起人对象永远不会处于无效或不自恰的状态上,这样作的短处是成功的操作必须执行两次。
如果操作的成功率较低的话,这样做就比较划算,反之就不太划算。
使用备忘录模式的优点和缺点
一、备忘录模式的优点
1、有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时,
使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
2、本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需
要的这些状态的版本。
3、当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
二、备忘录模式的缺点:
1、如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
2、当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法
提醒用户一个操作是否很昂贵。882——P
3、当发起人角色的状态改变的时候,有可能这个协议无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。
(1)宽接口和白箱:
public class Client { private static Originator originator = new Originator(); private static Caretaker c = new Caretaker(); public static void main(String[] args) { // 该发起人对象的状态 originator.setState("On"); // 创建备忘录对象,并将发起人对象的状态存储起来 c.saveMemento(originator.createMemento()); // 修改发起人对象的状态 originator.setState("Off"); // 恢复发起人对象的状态 originator.restoreMemento(c.retrieveMemento()); } }
// 发起人角色 class Originator { private String state; // 工厂方法,返还一个新的备忘录对象 public Memento createMemento() { return new Memento(state); } // 将发起人恢复到备忘录对象所记载的状态 public void restoreMemento(Memento memento) { this.state = memento.getState(); } // 状态的取值方法 public String getState() { return this.state; } // 状态的赋值方法 public void setState(String state) { this.state = state; System.out.println("Current state = " + this.state); } }
/* * 备忘录模式要求备忘录对象提供两个不同的接口:一个宽接口提供给发起人对象,另一个窄接口提供给所有其他的对象,包括负责人对象。 * 宽接口允许发起人读取到所有的数据;窄接口只允许它把备忘录对象传给其他的对象而看不到内部的数据。 */ // 备忘录角色 class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return this.state; } public void setState(String state) { this.state = state; } }
/* * 负责人角色负责保存备忘录对象,但是从不修改(甚至不查看)备忘录对象的内容(一个更好的实现是负责人对象根本无法从备忘录 对象中读取个修改其内容) */ // 负责人角色 class Caretaker { private Memento memento; // 备忘录的取值方法 public Memento retrieveMemento() { return this.memento; } // 备忘录的赋值方法 public void saveMemento(Memento memento) { this.memento = memento; } }
首先将发起人对象的状态设置成“On”(或者任何有效状态),并且创建一个备忘录对象将这个状态存储起来;然后将发起人对象的状态改成“Off”(或者任何状态);最后又将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态(或者先前所存储的任何状态)
备忘录系统运行的时序是这样的:
(1)将发起人对象的状态设置成“On”。
(2)调用发起人角色的createMemento()方法,创建一个备忘录对象将这个状态存储起来。
(3)将备忘录对象存储到负责人对象中去。
备忘录系统恢复的时序是这样的:
(1)将发起人对象的状态设置成“Off”;
(2)将备忘录对象从负责人对象中取出;
(3)将发起人对象恢复到备忘录对象所存储起来的状态,“On”状态。
白箱实现的优缺点
白箱实现的一个明显的好处是比较简单,因此常常用做教学目的。白箱实现的一个明显的缺点是破坏对发起人状态的封装。
(2)窄接口或者黑箱实现:
//客户端 public class Client { private static Originator originator = new Originator(); private static Caretaker c = new Caretaker(); public static void main(String[] args) { // 该发起人对象的状态 originator.setState("On"); // 创建备忘录对象,并将发起人对象的状态存储起来 c.saveMemento(originator.createMemento()); // 修改发起人对象的状态 originator.setState("Off"); // 恢复发起人对象的状态 originator.restoreMemento(c.retrieveMemento()); } }
// 发起人角色 class Originator { private String state; public Originator() { } // 工厂方法,返还一个新的备忘录对象 public MementoIF createMemento() { return new Memento(this.state); } // 将发起人恢复到备忘录对象记录的状态 public void restoreMemento(MementoIF memento) { Memento aMemento = (Memento) memento; this.setState(aMemento.getState()); } public String getState() { return this.state; } public void setState(String state) { this.state = state; System.out.println("state =" + state); } protected class Memento implements MementoIF { private String savedState; public Memento(String someState) { this.savedState = someState; } private void setState(String someState) { savedState = someState; } private String getState() { return savedState; } } }
interface MementoIF { }
// 备忘录角色 class Memento implements MementoIF { private String state; public Memento(String state) { this.state = state; } public String getState() { return this.state; } public void setState(String state) { this.state = state; } }
class Caretaker { private MementoIF memento; public MementoIF retrieveMemento() { return this.memento; } public void saveMemento(MementoIF memento) { this.memento = memento; } }
黑箱实现运行时的时序为;
(1)将发起人对象的状态设置成“On”。
(2)调用发起人角色的 createMemento()方法,创建一个备忘录对象将这个状态存储起来。
(3)将备忘录对象存储到负责人对象中去。由于负责人对象拿到的仅是 MementoIF类型,因此无法读出备忘录内部的状态。
恢复时的时序为:
(1)将发起人对象的状态设置成“Off”;
(2)将备忘录对象从负责人对象中取出。注意此时仅能得到 MementoIF接口,因此无法读出此对象的内部状态
(3)将发起人对象的状态恢复成备忘录对象所存储起来的状态,,由于发起人对象的内部类Memento实现了MementoIF接口
这个内部类是传入的备忘录对象的真实类型,因此发起人对象可以利用内部类Memento 的私有 接口读出此对象的内部状态。
(3)存储多个状态的备忘录模式:
//发起人角色 import java.util.Vector; import java.util.Enumeration; public class Originator{ private Vector states; private int index; public Originator(){ states = new Vector(); index = 0; } public Memento createMementor(){ return new Mementor(states,index); } public void restoreMementor(Mementor memento){ states = memento.getStates(); index = memento.getIndex(); } public void setState(String state){ this.states.addElement(state); index ++; } //辅助方法,打印出所有的状态 public void printStates(){ System.out.println("Total number of states: " + index); for(Enumeration e = states.elements();e.hasMoreElements();){ system.out.println(e.nextElement()); } } }
//备忘录角色 import java.util.Vector; public class Memento{ private Vector states; private int index; //<span style="font-size: 14px; font-family: Arial, Helvetica, sans-serif;">备忘录的构造子克隆了传入的states,然后将克隆存入到备忘录对象内部,这是一个重要的细节,因为不这样的话,将会</span><pre title="备忘录(Memento Pattern)模式 【行为模式第一篇】" style="font-size: 14px;"> //将会造成客户端和备忘录对象持有对同一个Vector对象的引用,也可以同时修改这个Vector对象,会造成系统崩溃。
public Memento(Vector states,int index){this.states = (Vector)states.clone();this.index = index;}//状态取值方法Vector getStates(){return states;}//检查点取值方法int getIndex(){return this.index;}}
//负责人角色 import java.util.Vector; public class Caretaker{ private Originator o; private Vector mementos = new Vector(); private int current; public Caretaker(Originator o){ this.o = o; current = 0; } public int createMemento(){ Memento memento = o.createMemento(); mementos.addElement(memento); return current ++; } //将发起人恢复到某个检查点 public void restoreMemento(int index){ Memento memento = (Memento)mementos.elementAt(index); o.restoreMemento(memento); } //某个检查点删除 public void removeMemento(int index){ mementos.removeElementAt(index); } }
//客户端 public class Client{ private static Originator o = new Originator(); private static Caretaker c = new Caretaker(o); public static void main(String[] args){ //改变状态 o.setState("state 0"); //建立一个检查点 c.createMemento(); //改变状态 o.setState("state 1"); c.createMemento(); o.setState("state 2"); c.createMemento(); o.setState("state 3"); c.createMemento(); o.setState("state 4"); c.createMemento(); o.printStates(); //恢复到第二个检查点 System.out.println("Restoring to 2"); c.restoreMemento(2); o.printStates(); System.out.println("Restoring to 0"); c.restoreMemento(0); o.printStates(); System.out.println("Restoring to 3"); c.restoreMemento(3); o.printStates(); } }
(4)自述历史模式(备忘录模式的一个变种)
由于“自述历史”作为一个备忘录模式的特殊实现形式非常简单易懂,它可能是备忘录模式最为流行的实现形式。
//窄接口 public interface MementoIF{}
//发起人角色 public class Originator{ public String state; public Originator(){} public void changeState(String state){ this.state = state; System.out.println("State has been changed to : " + state); } public Memento createMemento(){ return new Memento(this); } public void restoreMemento(MementoIF memento){ Memento m = (Memento)memento; changeState(m.state); } class Memento implements MementoIF{ private String state; private String getState(){ return state; } private Memento(Originator o){ this.state = o.state; } } }
//客户端 public class Client{ private static Originator o; private static MementoIF memento; public static void main(String args[]){ o = new Originator(); o.changeState("State 1"); memento = o.createMemento(); o.changeState("State 2"); o.restoreMemento(memento); } }