Java 中的备忘录模式:保存对象状态以进行撤销操作
大约 3 分钟
也称为
- 快照
- 令牌
备忘录设计模式的意图
Java 中的备忘录设计模式允许开发人员捕获和恢复对象的内部状态,而不会违反封装。
备忘录模式的详细解释以及现实世界示例
现实世界示例
文本编辑器应用程序可以使用 Java 中的备忘录设计模式来实现撤销和重做功能。通过在每次更改时捕获文档的当前状态作为备忘录,应用程序可以轻松地将文档恢复到任何先前状态。快照存储在历史记录列表中。当用户单击撤销按钮时,编辑器会将文档恢复到最近的备忘录中保存的状态。此过程允许用户恢复到文档的先前版本,而无需公开或更改编辑器的内部数据结构。
通俗地说
备忘录模式捕获对象内部状态,使其易于在任何时间点存储和恢复对象。
维基百科说
备忘录模式是一种软件设计模式,它提供将对象恢复到其先前状态的能力(通过回滚进行撤销)。
Java 中备忘录模式的编程示例
在我们的占星术应用程序中,我们使用备忘录模式来捕获和恢复星体对象的状态。每个状态都保存为一个备忘录,允许我们根据需要恢复到先前状态。
首先让我们定义我们可以处理的星体类型。
public enum StarType {
SUN("sun"),
RED_GIANT("red giant"),
WHITE_DWARF("white dwarf"),
SUPERNOVA("supernova"),
DEAD("dead star");
// ...
}
接下来,让我们直接跳到要点。以下是 Star
类以及我们需要操作的备忘录。尤其要注意 getMemento
和 setMemento
方法。
public interface StarMemento {
}
public class Star {
private StarType type;
private int ageYears;
private int massTons;
public Star(StarType startType, int startAge, int startMass) {
this.type = startType;
this.ageYears = startAge;
this.massTons = startMass;
}
public void timePasses() {
ageYears *= 2;
massTons *= 8;
switch (type) {
case RED_GIANT -> type = StarType.WHITE_DWARF;
case SUN -> type = StarType.RED_GIANT;
case SUPERNOVA -> type = StarType.DEAD;
case WHITE_DWARF -> type = StarType.SUPERNOVA;
case DEAD -> {
ageYears *= 2;
massTons = 0;
}
default -> {
}
}
}
StarMemento getMemento() {
var state = new StarMementoInternal();
state.setAgeYears(ageYears);
state.setMassTons(massTons);
state.setType(type);
return state;
}
void setMemento(StarMemento memento) {
var state = (StarMementoInternal) memento;
this.type = state.getType();
this.ageYears = state.getAgeYears();
this.massTons = state.getMassTons();
}
@Override
public String toString() {
return String.format("%s age: %d years mass: %d tons", type.toString(), ageYears, massTons);
}
private static class StarMementoInternal implements StarMemento {
private StarType type;
private int ageYears;
private int massTons;
// setters and getters ->
// ...
}
}
最后,以下是我们如何使用备忘录来存储和恢复星体状态。
public static void main(String[] args) {
var states = new Stack<StarMemento>();
var star = new Star(StarType.SUN, 10000000, 500000);
LOGGER.info(star.toString());
states.add(star.getMemento());
star.timePasses();
LOGGER.info(star.toString());
states.add(star.getMemento());
star.timePasses();
LOGGER.info(star.toString());
states.add(star.getMemento());
star.timePasses();
LOGGER.info(star.toString());
states.add(star.getMemento());
star.timePasses();
LOGGER.info(star.toString());
while (!states.isEmpty()) {
star.setMemento(states.pop());
LOGGER.info(star.toString());
}
}
程序输出
14:09:15.878 [main] INFO com.iluwatar.memento.App -- sun age: 10000000 years mass: 500000 tons
14:09:15.880 [main] INFO com.iluwatar.memento.App -- red giant age: 20000000 years mass: 4000000 tons
14:09:15.880 [main] INFO com.iluwatar.memento.App -- white dwarf age: 40000000 years mass: 32000000 tons
14:09:15.880 [main] INFO com.iluwatar.memento.App -- supernova age: 80000000 years mass: 256000000 tons
14:09:15.880 [main] INFO com.iluwatar.memento.App -- dead star age: 160000000 years mass: 2048000000 tons
14:09:15.880 [main] INFO com.iluwatar.memento.App -- supernova age: 80000000 years mass: 256000000 tons
14:09:15.880 [main] INFO com.iluwatar.memento.App -- white dwarf age: 40000000 years mass: 32000000 tons
14:09:15.881 [main] INFO com.iluwatar.memento.App -- red giant age: 20000000 years mass: 4000000 tons
14:09:15.881 [main] INFO com.iluwatar.memento.App -- sun age: 10000000 years mass: 500000 tons
何时在 Java 中使用备忘录模式
在以下情况下使用备忘录模式
- 您需要在 Java 中捕获对象的狀態並在之后恢复它,而无需公开其内部结构。这对于维护封装和简化对象状态管理至关重要。
- 直接获取状态的接口将公开实现细节并破坏对象的封装。
Java 中备忘录模式的现实世界应用
备忘录模式在各种 Java 应用程序中使用,包括 java.util.Date 和 java.util.Calendar 类,它们可以恢复到先前状态。它在文本编辑器和图形编辑器中也很常见,用于撤消机制。
备忘录模式的优点和权衡
优点
- 保留封装边界。
- 通过消除直接管理版本历史记录或撤销功能的需要,简化了发起者。
权衡
- 如果保存大量状态,则在内存方面可能很昂贵。
- 必须注意管理备忘录的生命周期,以避免内存泄漏。