备忘录模式
什么是备忘录模式
Java中的备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可以将对象恢复到原先保存的状态。
主要角色包括:
- 发起者(Originator):需要保存和恢复状态的对象。它记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,并可以访问备忘录里的所有信息。
- 备忘录(Memento):负责存储发起人的内部状态。它是一个临时中间对象,用于存储目标对象的初始相关属性信息。当需要恢复对象的状态时,备忘录提供这些内部状态给发起人。
- 看护者(Caretaker):对备忘录进行管理,提供保存与获取备忘录的功能。但它不能对备忘录的内容进行访问与修改。
优点:
- 状态保存与恢复:备忘录模式可以方便地保存和恢复对象的状态,使得对象的状态变化具有可追溯性。
- 封装性:通过将对象的状态封装在备忘录对象中,备忘录模式可以保持对象的封装性,不会暴露内部状态给外部对象。
- 简化撤销和重做操作:在需要实现撤销、重做等功能的场景中,备忘录模式可以大大简化操作,提高代码的清晰度和可维护性。
缺点:
- 资源消耗:如果对象的状态较为复杂或状态变化频繁,备忘录模式可能会消耗较多的内存资源来保存状态。
- 性能开销:频繁地创建和销毁备忘录对象可能会导致一定的性能开销。
- 设计复杂性:如果对象的状态需要保密或访问权限受限,备忘录模式可能会增加设计的复杂性,并可能破坏对象的封装性。
常见应用场景:
- 文本编辑器:在文本编辑器中,备忘录模式可以用于实现撤销和重做功能。当用户编辑文本时,编辑器可以定期保存文本状态到备忘录对象中。当用户需要撤销或重做操作时,编辑器可以恢复或重新应用这些状态。
- 游戏存档:在电子游戏中,备忘录模式可以用于实现游戏的存档功能。当玩家选择保存游戏时,游戏可以将当前的游戏状态(如玩家位置、分数、物品等)保存到备忘录对象中。当玩家再次加载游戏时,游戏可以从备忘录对象中恢复状态,让玩家继续之前的游戏进度。
- 数据库事务:在数据库操作中,备忘录模式可以用于实现事务的回滚功能。当执行一系列数据库操作时,可以将数据库的状态保存到备忘录对象中。如果事务执行失败或需要回滚,可以恢复到之前的状态。
案例
java实现控制台输入内容的回退
UML
实现步骤:
- 创建发起者ScannerInput,定义保存输入内容字段input
- 创建备忘录继承发起者,主要继承发起者的属性及get/set方法不再重复提供,提供创建备忘录及通过备忘录恢复状态的方法
- 创建看护者,定义栈对备忘录进行管理,提供保存与获取备忘录的功能。但它不能对备忘录的内容进行访问与修改
实现代码
ScannerInput.java
// 发起者
public class ScannerInput {
// 输入内容
protected String input;
public ScannerInput(String input) {
this.input = input;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
}
Originator.java
// 备忘录
// 继承发起者,主要继承发起者的属性及get/set方法不再重复提供
// 提供创建备忘录及通过备忘录恢复状态的方法
public class Originator extends ScannerInput{
public Originator(String input) {
super(input);
}
// 创建备忘录
public ScannerInput createMemento() {
return new ScannerInput(this.input);
}
// 恢复
public void restoreMemento(ScannerInput scannerInput) {
this.input = scannerInput.getInput();
}
}
Caretaker.java
import java.util.Stack;
// 看护者
// * 对备忘录进行管理,提供保存与获取备忘录的功能。但它不能对备忘录的内容进行访问与修改。
public class Caretaker {
// 定义栈管理备忘录
private Stack<ScannerInput> stack = new Stack<ScannerInput>();
// 压栈
public void setScannerInput(ScannerInput scannerInput) {
stack.push(scannerInput);
}
// 出栈
public ScannerInput getScannerInput() {
if(stack.size() == 0){
System.out.println("已经不可以回退啦!");
}
return stack.pop();
}
}
TestClient.java
import java.util.Scanner;
public class TestClient {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一些文本,输入'exit'退出:");
// 创建原发器对象并设置默认内容
Originator originator = new Originator("");
// 创建并保存备忘录
Caretaker caretaker = new Caretaker();
// 初始化压栈
caretaker.setScannerInput(originator.createMemento());
while (true) {
String input = scanner.nextLine();
if ("exit".equalsIgnoreCase(input)) {
System.out.printf("你输入了:%s%n" , input);
break;
}
// 回退操作
else if ("back".equalsIgnoreCase(input)) {
// 回退
originator.restoreMemento(caretaker.getScannerInput());
System.out.printf("回退上一步-你输入:%s%n 输入:back(回退上一步) exit(退出)%n" , originator.getInput());
} else {
// 设置内容
originator.setInput(input);
// 设置备忘录
caretaker.setScannerInput(originator.createMemento());
System.out.printf("你输入了:%s%n 输入:back(回退上一步) exit(退出)%n" , originator.getInput());
}
}
scanner.close();
System.out.println("已退出。");
}
}
执行结果: