备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

适用场景:

1、保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态;
2、对一个提供对象提供可回滚(rollback)的操作。

通用类图:

 对于备忘录,大家都很熟悉了,就是保存一份对象副本,在需要的时候再取出来回复为原来的状态。关于回滚(rollback),我们也在关系数据库中的事务机制中有所学习了。

上面的类图中,Originator 类是需要被保存的角色,它应该具有自己的内部状态、属性和数据等;而 Memento 类则是用于保存某一时刻下 Originator 副本的角色;Caretaker 类则是一个备忘录管理角色,Memento 类恢复一个自身的副本时应该通过 Caretaker 来实现。

按照类图用代码实现一下:

// 原始对象角色
class Originator {
	private String state;
	
	public Originator(String state) {
		this.setState(state);
	}
	
	public String getState() {
		return this.state;
	}
	
	public void setState(String state) {
		this.state = state;
	}
	
	// 创建备忘录,并备份当前状态
	public Memento createMemento() {
		return new Memento(this.state);
	}
	
	// 恢复备忘录中的备份状态
	public void rollbackState(Memento memento) {
		this.setState(memento.getState());
	}
}
// 备忘录角色
class Memento {
	private String state;
	
	public Memento(String state) {
		this.state = state;
	}
	
	public String getState() {
		return this.state;
	}
	
	public void setState(String state) {
		this.state = state;
	}
}
// 备忘录管理者角色
class Caretaker {
	private Memento memento;
	
	public Memento getMemento() {
		return this.memento;
	}
	
	public void setMemento(Memento memento) {
		this.memento = memento;
	}
}
// 测试类
public class Client {
	public static void main(String[] args) {
		
		Originator originator = new Originator("原来的状态 ...");
		System.out.println(originator.getState());
		
		// 创建备份管理者并进行备份
		Caretaker caretaker = new Caretaker();
		caretaker.setMemento(originator.createMemento());
		
		originator.setState("修改后的状态 ...");
		System.out.println(originator.getState());
		
		// 恢复状态
		originator.rollbackState(caretaker.getMemento());
		System.out.println(originator.getState());
	}
}

测试结果:
原来的状态 ...
修改后的状态 ...
原来的状态 ...

看看上面的代码,觉得没什么特别,感觉不像是一个设计模式,原因是挺简单的。不过,在真正运用这 Memento 模式时,难度应该会相当大。对比一下关系数据库中的事务机制,要实现回滚(rollback),RDBMS 在后台做了多少用户不了解的工作,一个个记录点的正确选择、判断发生故障的监测机制以及正确地恢复到提交前的状态等等…这些,马上就加大了 Memento 模式的使用了。
基于 Java 中的 clone 技术,让 Originator 直接实现 Cloneable 接口,实现其其中的 clone() 方法,类图如下:

   简单代码实现如下:
// 原始对象角色,其实是身兼多职,据所有功能于一身
class Originator implements Cloneable {
	
	// 保存备的份对象
	private Originator backup;
	
	private String state;
	
	public Originator(String state) {
		this.setState(state);
	}
	
	public String getState() {
		return this.state;
	}
	
	public void setState(String state) {
		this.state = state;
	}
	
	// 创建一个备份,即记录当前对象的拷贝
	public void createMemento() {
		this.backup = this.clone();
	}
	
	// 恢复一个备份,即获取backup的内部状态
	public void restoreMemento() {
		if(this.backup != null) {
			this.setState(this.backup.getState());
		}
	}
	
	/**
	 * 覆盖 clone() 方法,如果需要深拷贝,
	 * 就不能 这么简单地实现了,会复杂些
	 */
	@Override
	protected Originator clone() {
		try {
			return (Originator)super.clone();
		}catch(CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}
// 测试类
public class Client {
	public static void main(String[] args) {
		Originator originator = new Originator("原来的状态 ...");
		System.out.println(originator.getState());
		
		// 进行备份
		originator.createMemento();
		originator.setState("修改后的状态 ...");
		System.out.println(originator.getState());
		
		// 恢复备份
		originator.restoreMemento();
		System.out.println(originator.getState());
	}
}

测试结果:
 
原来的状态 ...
修改后的状态 ...
原来的状态 ...


虽然上面第二种实现偏离了 Memento 模式的定义,即“在该对象之外保存这个状态”,现在无论是原来的对象还是备份后的对象,甚至备份管理者 Caretaker 类都是在 Originator 类中,程序结构虽然简单,但如果 Originator 类中有多个状态,而且有多个引用变量时,实现 clone() 方法似乎也没有那么简单,因为要考虑到深拷贝,这就与 Prototype 模式(原型模式)有个共同需要考虑的方面了。

此外,也可以运用 Java 中的对象序列化、反序列化机制来实现备份的功能,可以将一个对象的内部状态序列化称为二进制流,保存到文件或数据库中,需要时再反序列化回来。不过,对于 Java 中对象的序列化、反序列化要注意序列化版本的兼容性,具体请参考相关书籍。要使一个对象可以被序列化,需要让某个类实现 java.lang.Serializable 接口,它是一个标志性接口,里面一个方法也没有,只要在类声明时加上 implements Serializable 即可。

我的相关文章:

(Prototype)原型模式的Java实现http://haolloyin.blog.51cto.com/1177454/333442