JAVA设计模式——备忘录模式

备忘录模式,又称快照模式(Snapshot Pattern),是一种行为型设计模式。其思想是:保存一个对象的某个状态,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便在适当的时候恢复对象

备忘录模式涉及到3个角色:

  • 备忘录(Memento):将发起人对象的内部状态存储起来。备忘录可提供两种接口:
    • 窄接口:只允许负责人对象(和其他除发起人对象之外的任何对象)把备忘录对象传给其他的对象。
    • 宽接口:与窄接口相反,这个宽接口允许负责人对象读取所有的数据。
  • 发起人(Originator):创建一个含有当前的内部状态的备忘录对象,并使用备忘录对象存储其内部状态。
  • 负责人(Caretaker):负责保存备忘录对象,但不检查备忘录对象的内容。


白箱备忘录模式

备忘录角色对任何对象都提供一个宽接口,备忘录角色的内部所存储的状态对所有对象公开。这种模式叫做白箱模式。

结构图:
JAVA设计模式——备忘录模式_第1张图片

具体代码实现:

备忘录:

public class Memento {
    private String state; 

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

发起人:

public class Originator {
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
        System.out.printf("state of originator now is %s\n", state);
    }

    // 创建备忘录
    public Memento createMemento() {
        return new Memento(this.state);
    }

    // 回滚到备忘录所记录的状态
    public void rollbackTo(Memento memento) {
        if (memento != null) this.setState(memento.getState());
    }
}

负责人:

public class Caretaker {
    // 持有一个备忘录引用,并且只维护备忘录,不维护备忘录中的具体内容
    private Memento memento; 

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

客户:

public class Client {
    private Originator originator = new Originator();
    private Caretaker caretaker = new Caretaker();

    // 设置一个状态,并记录到备忘录
    public void method1() {
        originator.setState("STATE 1");
        Memento memento = originator.createMemento();
        caretaker.setMemento(memento);
    }

    // 设置一个状态,然后回滚
    public void method2() {
        originator.setState("STATE 2");
        originator.rollbackTo(caretaker.getMemento());
    }
}

// 测试
class MementoTest {
    public static void main(String[] args) {
        Client client = new Client();
        client.method1();
        client.method2();
    }
}

运行结果:

state of originator now is STATE 1
state of originator now is STATE 2
state of originator now is STATE 1

可以看到,白箱模式是破坏封装性的,必须依靠程序员自律才能实现备忘录模式的用意。


黑箱备忘录模式

备忘录角色对发起人角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做黑箱模式,也叫自述历史模式。

JAVA中同时实现宽接口与窄接口的方法,我们在迭代子模式的文章里提到过了,就是使用私有的成员内部类方式。

结构图:
JAVA设计模式——备忘录模式_第2张图片

具体代码实现:

备忘录、发起人:

// 抽象备忘录,不提供对外的方法,只提供依赖倒置的作用
public interface Memento {
}

// 发起人
public class Originator {
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
        System.out.printf("state of originator now is %s\n", state);
    }

    // 创建备忘录
    public Memento createMemento() {
        return new Mem(this.state);
    }

    // 回滚到备忘录中记录的状态
    public void rollbackTo(Memento memento) {
        this.setState(((Mem) memento).state);
    }

    // 以私有成员内部类的方式提供具体备忘录,使备忘录的内容对发起人公开,对其它对象不公开
    private class Mem implements Memento {
        private String state;

        private Mem(String state) {
            this.state = state;
        }
    }
}

负责人、客户和测试的代码和白箱实现是一样的,不再贴出。

运行结果:

state of originator now is STATE 1
state of originator now is STATE 2
state of originator now is STATE 1


多检查点模式

上面两个例子都是只存储一个状态的简单实现,常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。

结构图:
JAVA设计模式——备忘录模式_第3张图片

具体代码实现:

// 抽象备忘录
public interface Memento {
}

// 发起人
public class Originator {
    private List list = new ArrayList<>(); // 历史状态列表
    private int index = 0; // 目录状态指针

    // 新增一个状态
    public void setState(String state) {
        this.list.add(state);
        this.index++;
    }

    // 打印状态列表
    public void printStates() {
        this.list.forEach(System.out::println);
    }

    // 创建一个备忘录
    public Memento createMemento() {
        return new Mem(this.list, this.index);
    }

    // 回滚到某个备忘录记录的状态
    public void rollbackTo(Memento memento) {
        Mem mem = (Mem) memento;
        this.list = mem.list;
        this.index = mem.index;
    }

    // 黑箱模式实现
    private class Mem implements Memento {
        private List list;
        private int index;

        private Mem(List list, int index) {
            // 注意这个坑!!像下面这样写,只是把Mem的list指针指向Originator的list指针指向的对象,
            // 当对象内容改变,也会体现在Mem的list指针上面,无法做到“记录”的效果。
            // 读者可以试试这样写最后结果会有什么不同。
            // this.list = list; 

            this.list = new ArrayList<>(list);
            this.index = index;
        }
    }
}

// 负责人
public class Caretaker {
    private Originator originator; // 持有一个发起人的引用
    private List list; // 持有一个备忘录列表的引用

    public Caretaker(Originator originator) {
        this.originator = originator;
        this.list = new ArrayList<>();
    }

    // 新建一个检查点
    public void createCheckPoint() {
        Memento memento = this.originator.createMemento();
        this.list.add(memento);
    }

    // 回滚到指定的检查点
    public void rollbackTo(int index) {
        Memento memento = list.get(index);
        this.originator.rollbackTo(memento);
    }
}

// 客户
public class Client {
    private Originator originator = new Originator();
    private Caretaker caretaker = new Caretaker(originator);

    // 新增若干检查点
    public void buildCheckPoints(int count) {
        for (int i = 0; i < count; i++) {
            String state = "STATE "+i;
            originator.setState(state);
            caretaker.createCheckPoint();
        }
        originator.printStates();
    }

    // 回滚到指定检点
    public void rollback(int toIndex) {
        caretaker.rollbackTo(toIndex);
        originator.printStates();
    }
}

// 测试
class MementoTest {
    public static void main(String[] args) {
        Client client = new Client();
        client.buildCheckPoints(5);
        System.out.println("==========");
        client.rollback(1);
    }
}

运行结果:

STATE 0
STATE 1
STATE 2
STATE 3
STATE 4
==========
STATE 0
STATE 1

备忘录模式给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。

备忘录模式,尤其是多检查点模式,会产生大量的备忘录对象,是比较消耗资源的。

你可能感兴趣的:(JAVA设计模式)