一、定义
在不破坏封装性的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态。 这样以后就可将该对象恢复到原先保存的状态。
二、示例
相信我们在工作中都用过word编辑器吧,word中有一项功能,当我们的编辑出错时,Ctrl+Z可以使我们回退到以前的文本,这样我们就可以在以前文本的基础上继续编辑,而不需要从头开始编辑。现在我们应用备忘录模式实现一下这个功能。
类图如下:
首先我们要创建一个文档编辑器,负责编辑文本,备份文本,及恢复备份的文本
package memento;
/**
* 文档编辑器
*/
public class Word {
private String text;//文本
//获取文本内容
public String getText() {
return text;
}
//编辑文本内容
public void setText(String text) {
this.text = text;
}
//创建一个备忘录,并且保存文本到备忘录中
public Memento createMemento(String text){
return new Memento(text);
}
//从备忘录中恢复文本
public void restoreText(Memento memento){
this.setText(memento.getText());
}
}
其次我们要创建一个备忘录类,保存我们备份的文本
package memento;
/**
* 备忘录类
*/
public class Memento {
private String text;//备份的文本
//构造方法,传入要备份的文本
public Memento(String text) {
super();
this.text = text;
}
//获取备份文本
public String getText() {
return text;
}
//备份文本到备忘录中
public void setText(String text) {
this.text = text;
}
}
我们还要创建一个备忘录管理类,来管理我们的备忘录
package memento;
import java.util.HashMap;
/**
* 备忘录管理类
*/
public class MementoManager {
private HashMap<String,Memento> mementos=new HashMap<String,Memento>();//保存备忘录,根据时间来区分备忘录
//添加备忘录
public void addMemento(String time,Memento memento){
if(this.mementos==null){
this.mementos=new HashMap<String,Memento>();
}
this.mementos.put(time,memento);
}
//根据时间获取备忘录
public Memento getMemento(String time){
return this.mementos.get(time);
}
}
测试类
package memento;
/**
* 场景类
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Word word=new Word();//新建一个文档
MementoManager manager=new MementoManager();//备份管理器
word.setText("第一行文本");//写入第一行文本
System.out.println("写入文本,当前文本:\n"+word.getText());
manager.addMemento("15:38",word.createMemento(word.getText()));//创建一个备份并添加到管理器中
System.out.println("======备份======");
word.setText("第一行文本\n第二行文本");//写入第二行文本
System.out.println("写入文本,当前文本:\n"+word.getText());
word.restoreText(manager.getMemento("15:38"));
System.out.println("恢复,当前文本:\n"+word.getText());
}
}
运行结果:
三、角色
通俗地说, 备忘录模式就是一个对象的备份模式, 提供了一种程序数据的备份方法。一般来说,有三种角色:
- Originator 发起人角色
记录当前时刻的内部状态, 负责定义哪些属于备份范围的状态, 负责创建和恢复备忘录数据。例如上例中的:Word.class。 - Cementor 备忘录角色
负责存储Originator发起人对象的内部状态, 在需要的时候提供发起人需要的内部状态。例如上例中的:Cementor.class。 - CementorManager 备忘录管理员角色
对备忘录进行管理、 保存和提供备忘录。例如上例中的:CementorManager.class。
四、使用场景
- 需要保存和恢复数据的相关状态场景。
- 提供一个可回滚( rollback) 的操作; 比如Word中的CTRL+Z组合键, IE浏览器中的后退按钮, 文件管理器上的backspace键等。
- 需要监控的副本场景中。 例如要监控一个对象的属性, 但是监控又不应该作为系统的主业务来调用, 它只是边缘应用, 即使出现监控不准、 错误报警也影响不大, 因此一般的做法是备份一个主线程中的对象, 然后由分析程序来分析。
五、注意事项
- 备忘录的生命期
备忘录创建出来就要在“最近”的代码中使用, 要主动管理它的生命周期, 建立就要使用, 不使用就要立刻删除其引用, 等待垃圾回收器对它的回收处理。 - 备忘录的性能
不要在频繁建立备份的场景中使用备忘录模式( 比如一个for循环中) , 原因有二: 一是控制不了备忘录建立的对象数量; 二是大对象的建立是要消耗资源的, 系统的性能需要考虑。
六、clone方式的备忘录
在原型模式的时候,我们可以通过复制的方法产生一个对象的内部状态,这个一个很好的方法,我们只需要发起者角色实现Cloneable接口:
package memento;
/**
* 实现Cloneable接口,既是发起者角色,也是备忘录角色
*/
public class CloneableWord implements Cloneable{
private String text;//文本
private CloneableWord backup;//备份对象
//获取文本内容
public String getText() {
return text;
}
//设置文本内容
public void setText(String text) {
this.text = text;
}
//创建备份
public void createMemento(){
this.backup=(CloneableWord) this.clone();//通过clone()方法复制对象的状态给备份对象
}
//从备份中恢复文本
public void restoreText(){
this.setText(this.backup.getText());//从备份对象恢复对象状态
}
@Override
protected Object clone() {
try{
return (CloneableWord)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
增加一个备份对象,通过clone()方法完整复制一个对象的状态到备份对象,需要的时候再通过备份对象还原。这样,CloneableWord既是发起人角色,也是备忘录角色。
package memento;
/**
* 场景类
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
CloneableWord word=new CloneableWord();//新建一个文档
word.setText("第一行文本");//写入第一行文本
System.out.println("写入文本,当前文本:\n"+word.getText());
word.createMemento();//备份
System.out.println("======备份======");
word.setText("第一行文本\n第二行文本");//写入第二行文本
System.out.println("写入文本,当前文本:\n"+word.getText());
word.restoreText();//恢复文本
System.out.println("恢复,当前文本:\n"+word.getText());
}
}
运行结果:
与前面实现方式完全一致。