行为型模式-备忘录模式
-
- 8.9备忘录模式
-
- 8.9.1概念
- 8.9.2场景
- [8.9.3优势 / 劣势](#8.9.3优势 / 劣势)
- 8.9.4备忘录模式可分为
- 8.9.5备忘录模式
- 8.9.6实战
- 8.9.7总结
8.9备忘录模式
8.9.1概念
备忘录模式允许在不暴露对象实现细节的情况下捕获和恢复对象的内部状态。通过将对象的状态封装在备忘录对象中,并将备忘录对象保存在一个管理者对象中,可以在需要时回滚到对象之前的状态。
8.9.2场景
在现代办公场景中,备忘录模式可以应用于文档编辑工具中。以Office工具为例,当用户在云文档中进行编辑时,系统会自动将当前的文档内容保存在一个备忘录对象中,并将备忘录对象存储在云端的服务器上。每隔一段时间,系统会判断是否需要保存文档的状态,如果需要,则将当前的备忘录对象保存为一个快照,以便日后恢复文档的状态。
8.9.3优势 / 劣势
- 提供恢复机制:当用户有需要时,能够比较方便地将数据恢复到某个历史的状态
- 实现内部封装:除了创建它的发起人之外,其他对象都不能够访问这些状态信息
- 资源消耗大:若需要保存的内部状态信息过多或者特别频繁是,将会占用比较大的内存资源
- 难以确定保存时间:当对象的状态发生改变时,需要判断是否需要存储备忘录
8.9.4备忘录模式可分为
- 发起人Originator:需要还原状态的那个对象,负责创建一个备忘录,并使用备忘录记录当前时刻的内部状态
- 备忘录Memento:存储发起人对象的内部状态,它可以包含发起人的部分或全部状态信息,但是对外部是不可见的,只有发起人能够访问备忘录对象的状态
备忘录有两个接口,发起人能够通过宽接口访问数据,管理者只能看到窄接口,并将备忘录传递给其他对象
- 管理者Caretaker:负责存储备忘录对象,但并不了解其内部结构,管理者可以存储多个备忘录对象
- 客户端:在需要恢复状态时,客户端可以从管理者哪里获取备忘录对象,并将其传递给发起人进行状态的恢复
8.9.5备忘录模式
java
package com.technologystatck.designpattern.mode.memorandum;
import java.util.ArrayList;
import java.util.List;
public class Memorandum {
public static void main(String[] args) {
//创建发起人对象
Originator originator = new Originator();
originator.setState("State 1");
//创建管理者对象
Caretaker caretaker = new Caretaker();
//保存当前状态
caretaker.addMemento(originator.createMemento());
System.out.println("Current State:"+originator.getState());
//修改状态
originator.setState("State 2");
System.out.println("Current State:"+originator.getState());
//再次保存当前状态
caretaker.addMemento(originator.createMemento());
//恢复到先前状态
originator.restoreFromMemento(caretaker.getMemento(0));
System.out.println("Current State:"+originator.getState());
}
}
//创建发起人类:可以创建备忘录对象
class Originator{
//备忘录的状态
private String state;
public void setState(String state){
this.state=state;
}
public String getState(){
return state;
}
//创建备忘录对象
public Memento createMemento(){
return new Memento(this.state);
}
//通过备忘录对象恢复状态
public void restoreFromMemento(Memento memento){
state=memento.getState();
}
}
//创建备忘录类:保存发起人对象的状态
class Memento{
private String state;
//保存发起人的状态
public Memento(String state){
this.state=state;
}
public String getState(){
return state;
}
}
//创建备忘录管理者类:保存备忘录对象
class Caretaker{
private List<Memento> mementos=new ArrayList<>();
//添加备忘录对象
public void addMemento(Memento memento){
mementos.add(memento);
}
//获取备忘录对象
public Memento getMemento(int index){
return mementos.get(index);
}
}
8.9.6实战
8.9.6.1题目描述
小明正在设计一个简单的计数器应用,支持增加(Increment)和减少(Decrement)操作,以及撤销(Undo)和重做(Redo)操作,请你使用备忘录模式帮他实现。
8.9.6.2输入描述
输入包含若干行,每行包含一个字符串,表示计数器应用的操作,操作包括 "Increment"、"Decrement"、"Undo" 和 "Redo"。
8.9.6.3输出描述
对于每个 "Increment" 和 "Decrement" 操作,输出当前计数器的值,计数器数值从0开始 对于每个 "Undo" 操作,输出撤销后的计数器值。 对于每个 "Redo" 操作,输出重做后的计数器值。
8.9.6.4代码
java
package com.technologystatck.designpattern.mode.memorandum;
import java.util.Scanner;
import java.util.Stack;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Counter counter = new Counter();
//处理计数器应用的输入
while (scanner.hasNext()) {
String operation = scanner.next();
switch (operation) {
case "Increment":
counter.increment();
break;
case "Decrement":
counter.decrement();
break;
case "Undo":
counter.undo();
break;
case "Redo":
counter.redo();
break;
}
System.out.println(counter.getValue());
}
}
}
//备忘录
class Memento {
//备忘录的状态
private int value;
public Memento(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
//创建人
class Counter {
//表示计数器的值
private int value;
//撤销操作
private Stack<Memento> undoStack = new Stack<>();
//重做操作
private Stack<Memento> redoStack = new Stack<>();
//减少计数器的值
public void increment() {
//执行撤销操作时,需要清除一些无效的备忘录对象
//这些备忘录对象失去作用了需要清空
redoStack.clear();
//需要将当前状态保存在撤销栈中
undoStack.push(new Memento(value));
//计数器的值加1
value++;
}
//增加计数器值
public void decrement() {
//执行重做操作时,需要清除一些无效的备忘录对象
//这些备忘录对象失去作用了需要清空
redoStack.clear();
//需要将当前状态保存在撤销栈中
undoStack.push(new Memento(value));
//计数器的值加1
value--;
}
/**
* 当执行撤销或重做的操作时,都会将相关的备忘录对象
* 移动到另一个栈中,保证了操作的一致性
*/
//撤销操作
public void undo() {
//若撤销栈不为空,则将当前栈顶元素移动到重做栈中,
//并将当前栈顶元素的值赋给value变量
if (!undoStack.isEmpty()) {
redoStack.push(new Memento(value));
value = undoStack.pop().getValue();
}
}
//重做操作
public void redo() {
//若重做栈不为空,则将当前栈顶元素移动到撤销栈中
//并将当前栈顶元素的值赋给value变量
if (!redoStack.isEmpty()) {
undoStack.push(new Memento(value));
value = redoStack.pop().getValue();
}
}
//获取value值
public int getValue() {
return value;
}
}
8.9.7总结
- 优点:简化了原始对象的代码结构,支持撤销和恢复操作
- 总结:将对象的状态使用栈或集合的形式保存起来,当需要撤销或重做时,就记录当前对象的状态,并将栈或集合中的值取出来
- 场景:适用于需要保证对象内部状态的封装和私有性前提下可以轻松地添加新的备忘录和发起人以此来实现备份