介绍
我们经常使用的办公软件都有回退功能,你曾想过这样的功能是如何实现的呢?相比软件的回退功能比较复杂,我们还是举个对象状态回退的功能,要回退状态,首先得记录对象的历史状态数据,当回退到某个状态时,能从数据存储的地方恢复到对象状态里。通常我们把对象的状态数据封装起来,外部对象无权访问内部状态,如果状态暴露就易违反封装特性,会对代码的可靠性和扩展性作出妥协。
备忘录模式可以在不破坏对象封装特性的前提下解决我们上面的问题,也不会暴露对象的内部结构。为了更好的理解备忘录模式,我们举个案例来说明,创建一个类,有两个Double 类型的字段,然后基于这两个字段进行数学运算,同时支持回退操作,回到上一个运算结果状态。
备忘录模式详解
备忘录模式的目的是不违反封装原则,去捕获对象的内部状态并把状态存储在外部,将来可以利用存储的数据来恢复对象的状态。
主要参与者如下图:
** Originator **
- 创建一个包含自己内部数据的备忘录(Memento)对象
- 利用备忘录对象来恢复内部状态
** Caretaker **
- 存储备忘录对象的集合
- 不对备忘录对象进行操作
当想保存 originator 的某时刻状态时,先获取内部状态然后用来组装 Memento 对象,最后把 memento 对象保存在 Caretaker 对象中的集合里。Memento 对象必须隐藏 originator 的内部数据,只提供get 方法获取值,防止外部对象修改 originator 对象存档的内部状态。
代码实现
public class Originator {
private double x;
private double y;
private String lastUndoSavePoint;
CareTaker careTaker;
public Originator(double x, double y, CareTaker careTaker) {
this.x = x;
this.y = y;
this.careTaker = careTaker;
createSavePoint("initial");
}
public void createSavePoint(String savePointName) {
careTaker.saveMemento(savePointName, new Memento(this.x, this.y));
this.lastUndoSavePoint = savePointName;
}
private void setOriginatorState(String savePointName){
Memento memento = careTaker.getMemento(savePointName);
this.x = memento.getX();
this.y = memento.getY();
}
public void undo(){
setOriginatorState(lastUndoSavePoint);
}
public void undo(String savePointName){
setOriginatorState(savePointName);
}
public void undoAll(){
setOriginatorState("initial");
careTaker.clearSavePoints();
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
@Override
public String toString() {
return "Originator{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class Memento {
private final double x;
private final double y;
public Memento(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
public class CareTaker {
private final Map<String, Memento> savePointStorage = new HashMap<>();
public void saveMemento(String savePointName, Memento memento) {
System.out.println("Save state..." + savePointName);
savePointStorage.put(savePointName, memento);
}
public Memento getMemento(String savePointName) {
System.out.println("Undo at..." + savePointName);
return savePointStorage.get(savePointName);
}
public void clearSavePoints(){
System.out.println("Clearing all save points...");
savePointStorage.clear();
}
}
public class TestMementoPattern {
public static void main(String[] args) {
CareTaker careTaker = new CareTaker();
Originator originator = new Originator(1, 2, careTaker);
originator.setY(6);
originator.createSavePoint("save1");
originator.undo();
System.out.println("State: " + originator);
originator.setX(10);
originator.createSavePoint("save2");
originator.setY(11);
originator.createSavePoint("save3");
originator.undo("save2");
System.out.println("State: " + originator);
}
}
输出结果:
Save state…initial
Save state…save1
Undo at…save1
State: Originator{x=1.0, y=6.0}
Save state…save2
Save state…save3
Undo at…save2
State: Originator{x=10.0, y=6.0}
需要记录某时刻对象状态的快照,并且将来能够复原的场景,需要考虑用备忘录模式,在Jdk 中使用此模式的例子
- java.util.Date
- java.io.Serializable