备忘录模式
备忘录模式,顾名思义为了就是实现一个“备忘”的功能而存在的。官话:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复至原先保存的状态。
三个角色
- Originator:发起人,就是想要实现备忘功能的角色
- Memento:备忘类,帮助实现备忘功能的角色,拥有和发起人一样的想要备份的属性,发起人通过备忘录类来恢复之前的数据
- CareTaker:用来保存备忘类的角色
栗子
有这样一个人,他想要自杀,自杀以后呢,他又后悔了,想复活,用备忘录模式来实现。
- 定义这个人
拥有“姓名”和“状态”两个属性,想要备份“状态”属性
/**
* @author river
* @date 2019-02-25 12:26
* 人
*/
public class Person {
private String name;
private State state;
/**
* 创建备忘对象
* @return
*/
public StateMemento createMemento() {
return new StateMemento(this);
}
/**
* 自杀
*/
public void suicide() {
this.state = State.DEAD;
display();
}
/**
* 后悔
* @param stateMemento
*/
public void regret(StateMemento stateMemento) {
this.state = stateMemento.getState();
display();
}
public void display() {
System.out.println(String.format("我是:%s,我现在状态是:%s", name, state.desc));
}
public String getName() {
return name;
}
public State getState() {
return state;
}
public Person(String name, State state) {
this.name = name;
this.state = state;
display();
}
public enum State {
ALIVE("alive", "活着"),
DEAD("dead", "死了");
private String name;
private String desc;
State(String name, String desc) {
this.name = name;
this.desc = desc;
}
}
}
- 定义备忘类
拥有发起人的“状态”属性,备份发起人的状态属性
/**
* @author river
* @date 2019-02-25 12:30
* 状态备忘类
*/
public class StateMemento {
private Person.State state;
public StateMemento(Person person) {
this.state = person.getState();
}
public Person.State getState() {
return state;
}
public void setState(Person.State state) {
this.state = state;
}
}
- 备忘类管理类
如果只实现一次的备忘,那么这个类可以省略
/**
* @author river
* @date 2019-02-25 12:35
* 备忘管理类,用来存放备忘类
*/
public class MementoManager {
private StateMemento stateMemento;
public MementoManager(StateMemento stateMemento) {
this.stateMemento = stateMemento;
}
public StateMemento getStateMemento() {
return stateMemento;
}
public void setStateMemento(StateMemento stateMemento) {
this.stateMemento = stateMemento;
}
}
- 具体使用
/**
* @author river
* @date 2019-02-25 12:37
* 主程序
*/
public class Main {
public static void main(String[] args) {
Person tom = new Person("Tom", Person.State.ALIVE);
System.out.println("------------------------------------");
System.out.println("存档");
MementoManager mementoManager = new MementoManager(tom.createMemento());
System.out.println("------------------------------------");
System.out.println("我现在不想活了,我自杀");
tom.suicide();
System.out.println("------------------------------------");
System.out.println("我现在又后悔了,我想复活");
tom.regret(mementoManager.getStateMemento());
}
}
- 结果如下
我是:Tom,我现在状态是:活着
------------------------------------
存档
------------------------------------
我现在不想活了,我自杀
我是:Tom,我现在状态是:死了
------------------------------------
我现在又后悔了,我想复活
我是:Tom,我现在状态是:活着
总结
优点:提供了一种可以恢复的机制,实现备份的功能
缺点:如果需要备份的属性太多,那么势必要占用更多的内存,每次保存都会多消耗资源
补充
实现多次备份,多次恢复,多次取消恢复
复杂一点的栗子
实现windows下Ctrl+z,Ctrl+y
- 定义一个操作类
把备忘录类当做操作类的内部类,定义了ctrlZ、ctrlY方法
/**
* @author river
* @date 2019-02-25 13:21
* 操作类
*/
public class Operation {
private String content;
private Integer currentStep;
public Operation(String content) {
this.content = content;
this.currentStep = 1;
}
public Memento createMemento() {
return new Memento(this);
}
/**
* 打字
* @param content
*/
public void type(String content) {
this.content += content;
this.currentStep++;
display();
}
/**
* 显示
*/
public void display() {
System.out.println(String.format("当前操作是第%s步,当前内容是:%s", currentStep, content));
}
public void ctrlZ(Memento memento) {
System.out.println("操作了Ctrl+z");
this.currentStep = memento.step;
this.content = memento.context;
display();
}
public void ctrlY(Memento memento) {
System.out.println("操作了Ctrl+y");
this.currentStep = memento.step;
this.content = memento.context;
display();
}
class Memento {
private String context;
private Integer step;
public Memento(Operation operation) {
this.context = operation.content;
this.step = operation.currentStep;
}
}
}
- 定义保存备忘录的类
用list来存储备忘录类,记录每一步的操作,设置一个游标cursor,来记录位置,每次undo操作,游标减1后取值,每次redo,游标加1后取值,每次保存save的时候删除掉当前游标后面的元素,然后再保存。
/**
* @author river
* @date 2019-02-25 13:33
* 备忘录类保存类
*/
public class Caretaker {
private List<Operation.Memento> mementos = new ArrayList<>();
private Integer cursor;
public Caretaker(Operation.Memento memento) {
mementos.add(memento);
cursor = 0;
}
public void save(Operation.Memento memento) {
if (cursor < mementos.size() - 1) {
this.mementos = this.mementos.subList(0, cursor + 1);
}
this.mementos.add(memento);
this.cursor++;
}
public Operation.Memento undo() {
if (cursor > 0) {
cursor--;
}
return this.mementos.get(cursor);
}
public Operation.Memento redo() {
if (cursor < mementos.size() - 1) {
cursor++;
}
return mementos.get(cursor);
}
}
- 编写测试
/**
* @author river
* @date 2019-02-25 13:45
* 主程序
*/
public class Main {
public static void main(String[] args) {
Operation operation = new Operation("我想打一些字");
Caretaker caretaker = new Caretaker(operation.createMemento());
operation.display();
operation.type(",再打几个字");
caretaker.save(operation.createMemento());
operation.type(",还打几个字");
caretaker.save(operation.createMemento());
operation.ctrlZ(caretaker.undo());
operation.type(",我又打了几个字");
caretaker.save(operation.createMemento());
operation.type(",最后再打几个字");
caretaker.save(operation.createMemento());
operation.ctrlZ(caretaker.undo());
operation.ctrlY(caretaker.redo());
operation.ctrlY(caretaker.redo());
operation.ctrlZ(caretaker.undo());
operation.ctrlZ(caretaker.undo());
operation.ctrlZ(caretaker.undo());
operation.ctrlZ(caretaker.undo());
}
}
- 运行结果
当前操作是第1步,当前内容是:我想打一些字
当前操作是第2步,当前内容是:我想打一些字,再打几个字
当前操作是第3步,当前内容是:我想打一些字,再打几个字,还打几个字
操作了Ctrl+z
当前操作是第2步,当前内容是:我想打一些字,再打几个字
当前操作是第3步,当前内容是:我想打一些字,再打几个字,我又打了几个字
当前操作是第4步,当前内容是:我想打一些字,再打几个字,我又打了几个字,最后再打几个字
操作了Ctrl+z
当前操作是第3步,当前内容是:我想打一些字,再打几个字,我又打了几个字
操作了Ctrl+y
当前操作是第4步,当前内容是:我想打一些字,再打几个字,我又打了几个字,最后再打几个字
操作了Ctrl+y
当前操作是第4步,当前内容是:我想打一些字,再打几个字,我又打了几个字,最后再打几个字
操作了Ctrl+z
当前操作是第3步,当前内容是:我想打一些字,再打几个字,我又打了几个字
操作了Ctrl+z
当前操作是第2步,当前内容是:我想打一些字,再打几个字
操作了Ctrl+z
当前操作是第1步,当前内容是:我想打一些字
操作了Ctrl+z
当前操作是第1步,当前内容是:我想打一些字