备忘录,顾名思义就是把当时的内容保存下来,以便日后使用。在软件设计模式中,备忘录模式就是指在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将这个对象恢复到保存的时候的状态。因为平时编程中会使用数据库作为保存数据的载体,所以备忘录模式并不会有太多的使用,大多数在文本处理和日志打印等场景中才会有所使用到。
备忘录模式讲解
- Memento:备忘录,主要用来存储原发器对象的内部状态。具体存储什么数据,是由原发器来决定的。备忘录可以是一个接口,也可以是一个存储对象的bean,作为接口的时候,原发器会继承这个接口,然后跳出一部分自己的数据创建一个备忘录来保存。作为对象bean的时候,通常是原发器持有这个对象,保存的时候会将这个对象的原型对象进行保存。
- Originator:原发器。使用备忘录来保存某个时刻的状态。
- Caretaker:备忘录的管理者,也就是保存备忘录的负责人。主要用于保存备忘录对象。
备忘录模式示例
我们模拟一个文本编辑器来举例一下备忘录的使用,因为文本就一个内容字段,我们直接创建一个备忘录的类TextMemento,让原发器持有这个对象,同时原发器还会实现保存当前内容和恢复内容的方法。具体代码如下:
1. 创建备忘录类TextMemento
因为内部只有一个String类型的方法,我们直接用jdk的clone方法就可以实现深度clone,获得其原型对象,具体内容请参考《原型模式——java实现原型模式的几种写法》:
/**
* 模拟文本编辑的类,在这里只有一个方法,我们用java自带的clone方法来获得原型对象
*/
public class TextMemento implements Cloneable {
public TextMemento(String content) {
this.content = content;
}
/**
* 内容
*/
private String content;
public void editContent(String content) {
this.content = content;
}
public void displayContent() {
System.out.println("当前的文本为:" + content);
}
@Override
protected TextMemento clone() throws CloneNotSupportedException {
return (TextMemento) super.clone();
}
}
2. 实现备忘录的管理者TextCaretaker,用一个栈来保存备忘录对象
/**
* 备忘录的管理者,我们用一个stack来管理存档的数据
*/
public class TextCaretaker {
private static final Stack<TextMemento> textMementoStack = new Stack<>();
public static void saveMemento(TextMemento textMemento) {
try {
TextMemento memento = textMemento.clone();
textMementoStack.push(memento);
} catch (Exception e) {
e.printStackTrace();
}
}
public static TextMemento getMemento() {
if (textMementoStack.empty()) {
System.out.println("没有可以恢复的数据");
return null;
}
return textMementoStack.pop();
}
}
3. 实现原发器TextOriginator
/**
* 文本的原发器,负责管理文本的保存和恢复
*/
public class TextOriginator {
private TextMemento currentMemento;
public TextOriginator(TextMemento textMemento) {
this.currentMemento = textMemento;
}
public void display() {
this.currentMemento.displayContent();
}
public TextMemento getCurrentMemento(){
return currentMemento;
}
/**
* 备份数据
*/
public void backupMemento() {
TextCaretaker.saveMemento(this.currentMemento);
}
/**
* 恢复数据
*/
public void recoverMemento() {
TextMemento memento = TextCaretaker.getMemento();
if (memento != null) {
this.currentMemento = memento;
}
}
}
4. 编写测试方法,测试备忘录的保存和恢复功能
public class Client {
@Test
public void testMemento(){
TextOriginator originator = new TextOriginator(new TextMemento("这是原始的数据"));
System.out.println("----------------原始数据并存档----------------------");
originator.display();
originator.backupMemento();
System.out.println("----------------第一次修改并存档----------------------");
originator.getCurrentMemento().editContent("第一次修改数据");
originator.display();
originator.backupMemento();
System.out.println("----------------第二次修改不存档----------------------");
originator.getCurrentMemento().editContent("第二次修改数据");
originator.display();
System.out.println("----------------第一次恢复后的数据----------------------");
originator.recoverMemento();
originator.display();
System.out.println("----------------第二次恢复后的数据----------------------");
originator.recoverMemento();
originator.display();
}
}
测试结果如下:
----------------原始数据并存档----------------------
当前的文本为:这是原始的数据
----------------第一次修改并存档----------------------
当前的文本为:第一次修改数据
----------------第二次修改不存档----------------------
当前的文本为:第二次修改数据
----------------第一次恢复后的数据----------------------
当前的文本为:第一次修改数据
----------------第二次恢复后的数据----------------------
当前的文本为:这是原始的数据
从上面的示例中,我们可以看到,备忘录的目标是保存对象的状态,保存是手段,恢复才是目标。在具体的实现中,一定要注意保存的是备忘录对象的原型对象,除非这种保存是持久化到持久化设备中,因为java对象是引用的方式,如果内部不是获取的原型对象,在原来对象的修改中同样会修改保存的对象,恢复的时候是达不到效果的。
4. 示例结构图如下
后记
个人总结,欢迎转载、评论、批评指正