《设计模式》备忘录模式

247 阅读4分钟

@TOC

定义

  • 备忘录模式又称为快照模式或者令牌模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
  • 属性行为型模式。

备忘录模式的角色组成

  • Originator(发起人):负责创建一个 Memento(备忘录),用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。
  • Memento(备忘录):负责存储 Originator(发起人) 对象的内部状态,并可防止发起人以外的其他对象访问备忘录。
  • Caretaker(管理者):负责存储、提供并管理备忘录,无法对备忘录的内容进行操作和访问。只负责存储对象,而不能修改对象,也无须知道对象的实现细节

访问者模式的 UML 类图

在这里插入图片描述

🎈情景案例:平时在写博客文章时,可能写到一半因为其他事中止了,这时通常将写好的内容保存到草稿(这时就是创建了一个备忘录),等有空时(或者是几天后几周后都可能)再从草稿箱中找回当时的草稿(恢复当时状态)继续撰写文章,这就是一个典型的备忘录模式的应用。

发起者 Originator 类

public class Originator {
    private String state;

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

    public Memento createMemento() {
        return new Memento(this);
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

备忘录 Memento 类

class Memento {
    private String state;

    public Memento(Originator originator) {
        state = originator.getState();
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

备忘录管理者 Caretaker 类

public class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

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

客户端 Client 类

public class Client {
    public static void main(String[] args) {
        // 创建发起者
        Originator originator = new Originator();
        originator.setState("文章进度50%");
        // 创建备忘录管理者
        Caretaker caretaker = new Caretaker();
        // 备忘录管理者暂存发起者目前的状态
        caretaker.setMemento(originator.createMemento());
        originator.setState("午休醒来朦胧状态接着午休前又写了10%,清醒之后发现写的不对,赶紧撤销修改~");
        // 撤销修改,恢复备忘录中保存的状态
        originator.setMemento(caretaker.getMemento());
    }
}

✨在设计备忘录类时需要考虑其封装性,除了 Originator 类,不允许其他类来调用备忘录类Memento 的构造函数与相关方法。如果不考虑封装性,允许其他类调用 setState() 等方法,将导致在备忘录中保存的历史状态发生改变,通过撤销操作所恢复的状态就不再是真实的历史状态,备忘录模式也就失去了本身的意义。

备忘录模式的优点

  • 实现了对信息的封装:将复杂的对象内部信息对其他的对象屏蔽,从而保持封装的边界。
  • 提供了对状态回滚的支持:使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。

备忘录模式的缺点

  • 资源消耗大:如果需要保存的状态过多,则每一次保存都会消耗很多内存。

备忘录模式的适用场景

  • 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时就能够恢复到先前的状态,实现撤销操作。
  • 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。

🎈备忘录模式在JDK源码 com.sun.corba.se.impl.encoding 中的应用

com.sun.corba.se.impl.encoding.CDRInputStream_1_0CORBA(Common Object Request Broker Architecture) 标准中定义的一种编解码器,它用于将Java对象序列化为二进制格式,以便在网络上进行传输。

CDRInputStream_1_0 中定义了一个名为 StreamMemento 的内部类,用于保存和恢复当前输入流的状态StreamMemento 类包含了输入流当前位置、缓冲区状态等信息,以便在需要的时候恢复输入流的状态。

CDRInputStream_1_0 源码

public class CDRInputStream_1_0 extends CDRInputStreamBase
    implements RestorableInputStream {
	
	// 此处省略若干行代码

    // Mark and reset -------------------------------------------------
    protected MarkAndResetHandler markAndResetHandler = null;

    protected class StreamMemento
    {
        // These are the fields that may change after marking
        // the stream position, so we need to save them.
        private int blockLength_;
        private int end_flag_;
        private int chunkedValueNestingLevel_;
        private int valueIndirection_;
        private int stringIndirection_;
        private boolean isChunked_;
        private javax.rmi.CORBA.ValueHandler valueHandler_;
        private ByteBufferWithInfo bbwi_;
        private boolean specialNoOptionalDataState_;

        public StreamMemento()
        {
            blockLength_ = blockLength;
            end_flag_ = end_flag;
            chunkedValueNestingLevel_ = chunkedValueNestingLevel;
            valueIndirection_ = valueIndirection;
            stringIndirection_ = stringIndirection;
            isChunked_ = isChunked;
            valueHandler_ = valueHandler;
            specialNoOptionalDataState_ = specialNoOptionalDataState;
            bbwi_ = new ByteBufferWithInfo(bbwi);
        }
    }

    public java.lang.Object createStreamMemento() {
        return new StreamMemento();
    }

    public void restoreInternalState(java.lang.Object streamMemento) {

        StreamMemento mem = (StreamMemento)streamMemento;

        blockLength = mem.blockLength_;
        end_flag = mem.end_flag_;
        chunkedValueNestingLevel = mem.chunkedValueNestingLevel_;
        valueIndirection = mem.valueIndirection_;
        stringIndirection = mem.stringIndirection_;
        isChunked = mem.isChunked_;
        valueHandler = mem.valueHandler_;
        specialNoOptionalDataState = mem.specialNoOptionalDataState_;
        bbwi = mem.bbwi_;
    }

    public int getPosition() {
        return get_offset();
    }

    public void mark(int readlimit) {
        markAndResetHandler.mark(this);
    }

    public void reset() {
        markAndResetHandler.reset();
    }
    // ---------------------------------- end Mark and Reset
}

CDRInputStream_1_0 中,备忘录模式的应用,可以让输入流在需要恢复状态时,能够快速且准确地恢复到之前保存的状态,提高编解码的效率和可靠性。