备忘录模式-撤销功能的实现

 在idea写代码的过程中,会经常用到一个快捷键——“crtl + z”,即撤销功能。“备忘录模式”则为撤销功能提供了一个设计方案。

1 备忘录模式

备忘录模式提供一种状态恢复机制。在不破坏封装的前提下,捕获对象内部状态并在该对象之外保存这个状态。可以在以后将对象恢复到原先保存的状态。

图 备忘录UML

Originator:原发器,一个需要保存内部状态的普通类,可以创建一个备忘录,也可通过备忘录来恢复其在某个时候的状态。

Memento:备忘录,存储原发器的内部状态。除了原发器本身及管理者类外,备忘录不能直接供其他类使用。

CareTaker:管理者,负责保存备忘录,但不能对备忘录内容进行操作或检查,无须知道备忘录的细节。

@Data
public class IdeaCodeOriginator {

    private String code;

    private Integer position;

    public IdeaCodeOriginator(String code, Integer position) {
        this.code = code;
        this.position = position;
    }

    public void restoreMemento(IdeaCodeMemento memento) {
        this.code = memento.getCode();
        this.position = memento.getPosition();
    }

    public IdeaCodeMemento createMemento() {
        return new IdeaCodeMemento(code,position);
    }

}

@Data
public class IdeaCodeMemento {

    private String code;

    private Integer position;

    public IdeaCodeMemento(String code, Integer position) {
        this.code = code;
        this.position = position;
    }
}

public class CareTaker {

    private final Stack mementoStack = new Stack<>();

    public IdeaCodeMemento getMemento(int num) {
        IdeaCodeMemento memento = null;
        while (num-- > 0 && !mementoStack.isEmpty()) {
            memento = mementoStack.pop();
        }
        return mementoStack.isEmpty() ? memento : mementoStack.pop();
    }

    public void saveMemento(IdeaCodeMemento memento) {
        mementoStack.push(memento);
    }

}

public class IdeaCodeEditor {

    public static void main(String[] args) {
        CareTaker careTaker = new CareTaker();

        IdeaCodeOriginator originator1 = new IdeaCodeOriginator("hello", 1);
        careTaker.saveMemento(originator1.createMemento());

        IdeaCodeOriginator originator2 = new IdeaCodeOriginator("word", 2);
        careTaker.saveMemento(originator2.createMemento());

        IdeaCodeOriginator originator3 = new IdeaCodeOriginator("and", 3);
        careTaker.saveMemento(originator3.createMemento());

        IdeaCodeOriginator originator4 = new IdeaCodeOriginator("java", 4);
        careTaker.saveMemento(originator4.createMemento());

        //撤回到2步前
        originator4.restoreMemento(careTaker.getMemento(2));
        System.out.println(originator4); // IdeaCodeOriginator(code=word, position=2)

    }

}

1.1 优化

1)要避免其他类操作或者检查备忘录的内容。

2)备忘录管理者应该具有通用性。

public interface Memento {
}

@Data
public class IdeaCodeOriginator {

    private String code;
    private Integer position;

    public IdeaCodeOriginator(String code, Integer position) {
        this.code = code;
        this.position = position;
    }

    @Data
    private static class InnerMemento implements Memento {
        private String code;
        private Integer position;

        public InnerMemento(String code, Integer position) {
            this.code = code;
            this.position = position;
        }
    }

    public Memento createMemento() {
        return new InnerMemento(code,position);
    }

    public void restoreMemento(Memento memento) throws Exception {
        if (memento instanceof InnerMemento) {
            InnerMemento innerMemento = (InnerMemento) memento;
            this.code = innerMemento.getCode();
            this.position = innerMemento.position;
        } else {
            throw new Exception("类型错误");
        }
    }

}

public class CareTaker {

    Stack stack = new Stack<>();

    public T getMemento(int num) {
        T memento = null;
        while (num-- > 0 && !stack.isEmpty()) {
            memento = stack.pop();
        }
        return stack.isEmpty() ? memento : stack.pop();
    }

    public void saveMemento(T memento) {
        stack.push(memento);
    }

}

public class IdeaCodeEditor2 {

    public static void main(String[] args) throws Exception {

        CareTaker objectCareTaker = new CareTaker<>();

        IdeaCodeOriginator originator1 = new IdeaCodeOriginator("hello", 1);
        objectCareTaker.saveMemento(originator1.createMemento());

        IdeaCodeOriginator originator2 = new IdeaCodeOriginator("word", 2);
        objectCareTaker.saveMemento(originator2.createMemento());

        IdeaCodeOriginator originator3 = new IdeaCodeOriginator("and", 3);
        objectCareTaker.saveMemento(originator3.createMemento());

        IdeaCodeOriginator originator4 = new IdeaCodeOriginator("java", 4);
        objectCareTaker.saveMemento(originator4.createMemento());

        originator4.restoreMemento(objectCareTaker.getMemento(3)); // IdeaCodeOriginator(code=hello, position=1)
        System.out.println(originator4);

    }

}

2 优缺点

如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。

优点:1)它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤;

缺点:1)资源消耗过大。如果要保存的原发器类的成员变量太多,就不可避免地占用大量的储存空间。每保存一次对象状态都需要消耗一定的系统资源。

3 适用场景

1)保存一个对象在某时刻的全部或部分状态,实现撤销操作。

2)防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态实现细节暴露给外界对象。

你可能感兴趣的:(设计模式的艺术,备忘录模式,设计模式,java)