《设计模式》备忘录模式
定义:
- 备忘录模式又称为快照模式或者令牌模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
- 属性行为型模式。
备忘录模式的角色组成:
- 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()
等方法,将导致在备忘录中保存的历史状态发生改变,通过撤销操作所恢复的状态就不再是真实的历史状态,备忘录模式也就失去了本身的意义。
备忘录模式的优点:
- 实现了对信息的封装:将复杂的对象内部信息对其他的对象屏蔽,从而保持封装的边界。
- 提供了对状态回滚的支持:使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
备忘录模式的缺点:
- 资源消耗大:如果需要保存的状态过多,则每一次保存都会消耗很多内存。
备忘录模式的适用场景:
- 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时就能够恢复到先前的状态,实现撤销操作。
- 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
🎈备忘录模式在工具包 com.sun.corba.se.impl.encoding 中的引用
com.sun.corba.se.impl.encoding.CDRInputStream_1_0
是 CORBA(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
中,备忘录模式的应用,可以让输入流在需要恢复状态时,能够快速且准确地恢复到之前保存的状态,提高编解码的效率和可靠性。