备忘录模式
备忘录模式是设计模式中的对象行为型模式,主要是为了在不破坏封装性的前提下,获取一个对象的内部状态,并且在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态了。
说到备忘录模式,我就想到了我们玩单机游戏的时候经常会存档,然后在一段时间之后可以读取存档的游戏内容然后从存档点开始继续游戏;还有我们使用Word软件等等都可以使用Ctrl+Z使内容恢复到之前的某个点。
备忘录模式的适用性
- 备忘录模式适用于必须要保存一个对象在某个时刻的状态(快照),这样方便于在以后需要它的时候能够马上恢复到要保存的状态的情况下。
- 备忘录模式适用于在只允许状态保存者访问保存的相关信息,而不是提供一个接口让其他所以对象来直接得到这个状态的情况下,
备忘录模式的结构图
备忘录模式中总共有三类角色:
1、原发器(Originator):原发器用于创建一个备忘录,并且记录此刻它自己的内部状态,同时也提供使用备忘录来恢复保存点时的状态。
2、备忘录(Memento):备忘录用于存储原发器的内部状态,同时原发器也提供外部访问备忘录的结构,我们将这个接口分为宽接口和窄接口,宽接口用于提供原发器访问备忘录存储的信息,窄接口提供给其他对象访问备忘录对象。
3、负责人(Caretaker):负责人的作用是保存好备忘录对象,在原发器需要恢复状态时从负责人对象中渠道保存好的备忘录进行备份。
备忘录模式的基础示例
原发器包含了自身的状态,同时还有保存当前状态、恢复状态等基本方法。
public class Originator {
public int state;
public Originator(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Memento createMemento() {
return new Memento(this.state);
}
public void setMemento(Memento memento) {
this.state = memento.getState();
}
public void printState() {
System.out.println("原发器角色的当前状态为:" + this.state);
}
}
备忘录保存原发器中的信息
public class Memento {
public int state;
public Memento(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
负责人保存备忘录即可
public class Caretaker {
public Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
客户端
public class Client {
public static void main(String[] args) {
Originator originator = new Originator(0);
Caretaker caretaker = new Caretaker();
//输出当前原发器状态
originator.printState();
//保存状态
caretaker.setMemento(originator.createMemento());
originator.setState(1);
//输出原发器改变状态之后的状态
originator.printState();
//恢复状态
originator.setMemento(caretaker.getMemento());
//输出原发器恢复状态之后的状态
originator.printState();
}
}
/*
执行结果:
原发器角色的当前状态为:0
原发器角色的当前状态为:1
原发器角色的当前状态为:0
*/
上面演示的就是备忘录模式的最基本实现,客户端实现了原发器的创建,保存原发器原始状态、改变原发器状态、恢复原发器状态的整个过程。但是这种实现方式我们会发现一个问题,原发器可以访问备忘录中的所有信息,负责人等其他对象其实也可以访问备忘录中的信息,因为备忘录中的方法、属性都是public的,备忘录模式的宽接口、窄接口的特点只是我们考代码的约束来实现的。那么,如何不通过代码的约束来实现宽窄接口的特点呢?
备忘录模式示例优化
实际上要实现备忘录的属性方法只提供给原发器来访问,一种方法就是讲备忘录类定义成原发器的私有内部类,这样其他类则不可能能够访问到备忘录的信息,那么备忘录定义成私有内部类,负责人信息如何保存备忘录呢?这个只需要备忘录继承一个空的接口,这样负责人类就访问不了具体备忘录的信息了。具体代码如下:
原发器和备忘录
public class Originator {
public int state;
public Originator(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Memento createMemento() {
return new Memento(this.state);
}
public void setMemento(MementoIF memento) {
this.state = ((Memento) memento).getState();
}
public void printState() {
System.out.println("原发器角色的当前状态为:" + this.state);
}
private class Memento implements MementoIF {
private int state;
private Memento(int state) {
this.state = state;
}
private int getState() {
return state;
}
private void setState(int state) {
this.state = state;
}
}
}
负责人
public class Caretaker {
public MementoIF memento;
public MementoIF getMemento() {
return memento;
}
public void setMemento(MementoIF memento) {
this.memento = memento;
}
}
客户端
public class Client {
public static void main(String[] args) {
Originator originator = new Originator(0);
Caretaker caretaker = new Caretaker();
//输出当前原发器状态
originator.printState();
//保存状态
caretaker.setMemento(originator.createMemento());
originator.setState(1);
//输出原发器改变状态之后的状态
originator.printState();
//恢复状态
originator.setMemento( caretaker.getMemento());
//输出原发器恢复状态之后的状态
originator.printState();
}
}
/*
运行结果:
原发器角色的当前状态为:0
原发器角色的当前状态为:1
原发器角色的当前状态为:0*/
客户端调用方式和结果与基本实现模式一样,只是在原发器和负责人类中的实现有细微的区别。
备忘录模式的优缺点
1、给用户提供了一种保存恢复机制。
2、保持了封装的边界,原发器保存的具体信息只有原发器能够获得。
3、如果用户需要频繁的使用备忘录模式,那么需要考虑会产生巨大的性能开销。