GoF的备忘录模式 ,对于Java语言的实现,并非令人喜欢——C++的味道太浓。在简单的例子中,Caretaker=Test或Client。
4.5备忘录模式(5.6) 给出了一个简明的实现:Originator提供设置还原点方法setRestorePoint()和回滚方法undo(),外界如Test仅仅依赖于原始对象Originator,而Memento和Warehouse(原文类名为Caretaker)都是包级私有类。
GoF的备忘录模式 中,Caretaker/保管员到底是一个什么角色呢?相当于上面的Warehouse,还是另有目的?
实际上,Caretaker不仅作为受到限制的Warehouse,还处理undo()的整个流程。
①由于undo()的整个流程由Caretaker处理,Originator需要提供工厂方法createMemento(),Caretaker设置还原点时指定Memento对象作为参数。
②窄接口与宽接口
Warehouse不关心(不查看也不修改)备忘录对象的内容;而Caretaker需要设计成它想关心也没有办法关心。Caretaker只知道备忘录是一个IMemento对象——具有窄接口,只有Originator能够访问Memento对象——具有宽接口。为了在严苛的条件下演示,将Caretaker与Originator放在同一个包中。但是,既然防火防盗防Caretaker,将它甩到包外(和Test同一个包),显然更合理,此时也就不需要IMemento这个标识接口了,Java的封装为不同位置的用户类形成了不同的接口——窄接口与宽接口。参考《编程导论(Java)·6.1封装性》
下面的代码按照GoF的方案实现,可以说是用Java实现C++的思路。
package intent.memento.black;
public interface IMemento{//将Caretaker甩到包外,本接口可以不要
}
Originator有私有内部类,按照GoF的方案,Originator需要提供工厂方法createMemento(),Caretaker设置还原点时需要指定Memento对象作为参数。
package intent.memento.black;
import obj.State;
/**
* Originator
*
* @author (yqj2065)
* @version (0.1)
*/
public class Originator{
private State state = new State();//要记忆的状态。
public static int index = 0;
public Originator(int x,int y){
state.setX(x);
state.setY(y);
}
public void modifyState(int x,int y){
state.setX(x);
state.setY(y);
}
public IMemento createMemento(){
return new Memento(state);
}
public void setMemento(IMemento memento){
this.state = ((Memento)memento).getState();
}
@Override public String toString(){
return ""+state;
}
/**
* 备忘录成为Originator的私有内部类。
*
*/
private class Memento implements IMemento{
private final State state;
private Memento(State state){
this.state = new State(){{setX(state.getX());setY(state.getY());}};
}
private State getState(){
return state;
}
}
}
Caretaker不仅作为受到限制的Warehouse,还处理undo()的整个流程。其中的包级私有方法可以合并到接口中。
package intent.memento.black;
import java.util.*;
public class Caretaker{
public Originator originator = new Originator(8,8);
private List<IMemento> mementos = new ArrayList<>();;
private int current = 0;//当前Memento2的存储号/指针
void save(IMemento m){
mementos.add(m);
current++;
}
IMemento getMem(){
current--;
return mementos.get(current);
}
/**
* 返回第index个Memento2对象。
*/
IMemento getMem(int index){
current=index;//当前指针 是否需要变化?
return mementos.get(current);
}
/**
* 设置还原点。
*/
public int setRestorePoint(IMemento memento ){
this.save(memento);
return current;
}
public void undo(){
IMemento memento = getMem();
originator.setMemento(memento);
}
/**
* 恢复到第index个还原点。
*/
public void undo(int index){
IMemento memento = getMem(index);
originator.setMemento(memento);
}
}
Test在另外的包intent.memento中。
package intent.memento;
import intent.memento.black.Caretaker;
import static tool.Print.*;
/**
* Write a description of class Test here.
*
* @author (yqj2065)
* @version (0.1)
*/
public class Test{
public static void testBlack(){
Caretaker c = new Caretaker();
intent.memento.black.Originator o = c.originator; pln(" "+o);
o.modifyState(5,7);pln("→ "+o);
o.modifyState(5,6);pln("→ "+o);
c.setRestorePoint(o.createMemento());pln("→* "+o);
o.modifyState(5,5);pln("→ "+o);
o.modifyState(5,4);pln("→ "+o);
c.setRestorePoint(o.createMemento());pln("→* "+o);
o.modifyState(4,4);pln("→ "+o);
c.setRestorePoint(o.createMemento());pln("→* "+o);
o.modifyState(3,3);pln("→ "+o);
c.undo(); pln("undo→ "+o);
c.undo(); pln("undo→ "+o);
int i=2;
c.undo(i); pln("undo("+i+")→"+o);
c.undo(); pln("undo→ "+o);
}
}
输出:
x=8 y=8
→ x=5 y=7
→ x=5 y=6
→* x=5 y=6
→ x=5 y=5
→ x=5 y=4
→* x=5 y=4
→ x=4 y=4
→* x=4 y=4
→ x=3 y=3
undo→ x=4 y=4
undo→ x=5 y=4
undo(2)→x=4 y=4
undo→ x=5 y=4