再谈备忘录模式 (memento pattern) Java

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值