简述
备忘录模式的用意是在不破坏封装性的条件下,将对象的状态存储起来,从而在未来合适的时候将对象的状态恢复到从前(某个检查点)。
角色
1.备忘录(Memento)角色
1)将发起人对象的内容状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。
2)备忘录可以保护其内容不被发起人对象之外的任何对象所读取。
3)备忘录有两个等效的接口:
窄接口(narrowinterface):负责人对象看到的是备忘录的窄接口,这个窄接口只允许它把备忘录对象传给其他的对象;
宽接口(wideinterface):与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
2.发起人(Originator)角色
1)创建一个含有当前内部状态的备忘录对象。
2)使用备忘录对象存储其内部状态。
3.负责人(Caretaker)角色
1)负责保存备忘录对象。
2)不检查备忘录对象的内容。
源代码
版本1.“白箱”备忘录模式的实现
Java中,实现“宽”和“窄”两个接口并不容易,如果暂时忽略两个接口的区别,仅为备忘录角色提供一个宽接口的话,备忘录的内部存储状态就对所有对象公开,这就是“白箱实现”。
“白箱”实现破坏了封装性,但是通过程序员自律,可以方便地实现备忘录模式。
“白箱”备忘录模式的类图如下:
“白箱”备忘录模式的Java实现如下:
package备忘录模式;
publicclassMemento{
privateStringstate;
publicMemento(Stringstate){
this.state=state;
}
publicStringgetState(){
returnthis.state;
}
publicvoidsetState(Stringstate){
this.state=state;
}
}
package备忘录模式;
publicclassOriginator{
privateStringstate;
//工厂方法,返回一个新的备忘录对象
publicMementocreateMemento(){
returnnewMemento(state);
}
//将发起人恢复到备忘录对象所记载的状态
publicvoidrestoreMemento(Mementomemento){
this.state=memento.getState();
}
publicStringgetState(){
System.out.println("Currentstate:"+state);
returnthis.state;
}
publicvoidsetState(Stringstate){
this.state=state;
}
}
package备忘录模式;
publicclassCaretaker{
privateMementomemento;
publicMementoretrieveMemento(){
returnthis.memento;
}
publicvoidsaveMemento(Mementomemento){
this.memento=memento;
}
}
package备忘录模式;
publicclassClient{
publicstaticvoidmain(String[]args){
Originatoro=newOriginator();
Caretakerc=newCaretaker();
//改变发起人的状态
o.setState("on");
o.getState();
//创建备忘录对象,并将发起人对象的状态存储起来
c.saveMemento(o.createMemento());
//再次改变发起人对象的状态
o.setState("off");
o.getState();
//恢复发起人对象的状态
o.restoreMemento(c.retrieveMemento());
o.getState();
}
}
输出:
Currentstate:on
Currentstate:off
Currentstate:on
版本2.“黑箱”备忘录模式的实现
将Memento设成Originator类的内部类;
将Memento的方法全部设成私有方法,这样只有它自己和发起人Originator可以调用;
在外部提供一个标识接口MementoIF给Caretaker以及其他对象,标识接口MementoIF没有提供任何方法,因此对外部来说Memento对象的内容都是不可见的。具体源代码见版本3中多个检查点的实现。
版本3.实现多个检查点的备忘录模式。
与版本1相比:
第一,版本3使用“黑箱”备忘录模式实现。
第二,版本3增强了负责人的功能,客户端不操作发起人角色进行备忘录的创建和状态的恢复,而只需要调用负责人即可。因此负责人角色要持有一个发起人角色的引用。
第三,版本3有多个检查点。
版本3的类图如下:
版本3源代码如下:
package备忘录模式;
//标识接口(窄接口)
publicinterfaceMementoIF{
}
package备忘录模式;
importjava.util.Vector;
publicclassOriginator{
privateVector<Object>states;
privateintindex;
publicOriginator(){
states=newVector<Object>();
index=0;
}
//工厂方法,返回一个新的备忘录对象
publicMementoIFcreateMemento(){
returnnewMemento(this.states,index);
}
//将发起人恢复到备忘录对象所记载的状态
publicvoidrestoreMemento(MementoIFmemento){
states=((Memento)memento).getStates();
index=((Memento)memento).getIndex();
}
//状态的赋值方法
publicvoidsetState(Objectstate){
this.states.addElement(state);
index++;
}
//辅助方法,打印出所有状态
publicvoidprintStates(){
System.out.println("Totalnumberofstates:"+index);
for(Objecto:states){
System.out.println(o.toString());
}
}
//内部类
protectedclassMementoimplementsMementoIF{
privateVector<Object>saveStates;
privateintsaveIndex;
@SuppressWarnings("unchecked")
//_states一定是Vector<Object类型的变量,复制后也一定是Vector<Object的变量
privateMemento(Vector<Object>_states,int_index){
//保存客户端传来的状态对象的拷贝,否则客户端的修改会影响到保存的状态。
saveStates=(Vector<Object>)_states.clone();
saveIndex=_index;
}
privateVector<Object>getStates(){
returnsaveStates;
}
privateintgetIndex(){
returnsaveIndex;
}
}
}
package备忘录模式;
importjava.util.Vector;
publicclassCaretaker{
privateOriginatoro;
privateVector<MementoIF>mementos=newVector<MementoIF>();
privateintcurrentIndex;
publicCaretaker(Originatoro){
this.o=o;
currentIndex=0;
}
//创建一个新的检查点
publicvoidcreateMemento(){
mementos.addElement(o.createMemento());
currentIndex++;
}
//将发起人恢复到某个检查点
publicvoidreStoreMemento(intindex){
o.restoreMemento(mementos.elementAt(index));
}
//删除某个检查点
publicvoidremoveMemento(intindex){
mementos.removeElementAt(index);
}
}
package备忘录模式;
publicclassClient{
publicstaticvoidmain(String[]args){
Originatoro=newOriginator();
Caretakerc=newCaretaker(o);
//改变发起人的状态
o.setState("state0");
//创建一个检查点
c.createMemento();
o.setState("state1");
c.createMemento();
o.setState("state2");
c.createMemento();
o.setState("state3");
c.createMemento();
o.setState("state4");
c.createMemento();
//打印出所有状态
o.printStates();
//恢复到第3个检查点
System.out.println("Restoringto3");
c.reStoreMemento(3);
o.printStates();
//恢复到第0个检查点
System.out.println("Restoringto0");
c.reStoreMemento(0);
o.printStates();
//恢复到第4个检查点
System.out.println("Restoringto4");
c.reStoreMemento(4);
o.printStates();
}
}
输出:
Totalnumberofstates:5
state0
state1
state2
state3
state4
Restoringto3
Totalnumberofstates:4
state0
state1
state2
state3
Restoringto0
Totalnumberofstates:1
state0
Restoringto4
Totalnumberofstates:5
state0
state1
state2
state3
state4
跟版本1相比,负责人角色除了负责保存状态之外,还负责发起人状态的恢复,功能增强了。
注意
1.以上的内容基本上都可以从阎宏的《Java与模式》找到,我只是“取其精华并整理之”。完成以上的工作后,我发现有些东西需要补充一下,以免有误导。
2.“黑箱”备忘录的实现中,将Memento类做成Originator的内部类,并将其方法全部设置成private,其实这样一般来说就已经足够了,不需要再使用窄接口MementoIF。因为这样做的话外部拿到Memento类的实例,由于其方法都是private的,所以该方法只有Originator类可以调用,其它类是调用不了的,也就无法修改其中的内容。
3.那么窄接口什么时候使用呢,我觉得应该是这样,如果Memento类因为某些原因不能做成内部类,那么就应该定义两个接口,一个WideMemento,一个NarrowMemento(一般没有定义任何方法),前者供Originator类使用,后者供其它类使用。这样的缺点就是,外部只要将得到的实例强制转化为WideMemento类型,同样可以访问到Memento类的内容。
4.如果要继承Originator类并且不改变Memento类的代码,那么Memento类的方法应该设置成默认属性(packageaccess),而不是private。
相关模式
命令模式:命令模式中使用备忘录模式来存储可撤销的操作的状态。