一、备忘录模式的核心思想
备忘录模式的主旨是存储一个对象的快照,即在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
备忘录模式是用一个备忘录对象来存储另外一个对象的内部状态,将该对象的状态捕捉住,并外部化存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。其中包含了3种重要的角色。
- 备忘录类 Memento:备忘录用来存储原始类的内部状态,原始类根据需要决定备忘录存储原始类的哪些内部状态,防止原始类以外的其他对象访问备忘录。
- 原始类 ClassA:创建一个备忘录对象,用以记录当前时刻它的内部状态,使用备忘录恢复内部状态。
- 备忘录存储类ClassB:负责保存好备忘录,不能对备忘录的内容进行操作或检查。
它们的关系如下图所示。
下面来看具体的实现。
(1) 备忘录类Memento.java的作用是用来存储一些对象的状态,因此它应该包含有需要存储状态的各种变量,这里我们以存储一个字符串变量的值为例,添加了一个 String 类型的变量 value。其源代码如下程序所示。
package behavior.memento;
/**
* @author Minggg
* 备忘录类
*/
public class Memento {
private String value;
public Memento(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
(2) 使用备忘录的类 ClassA.java 是一个普通类,为了存储它的某一种状态,需要提供一个创建备忘录对象的函数 createMemento(),并将当前的状态赋予该备忘录;同时需要提供一个从备忘录恢复数据的函数restoreMemento()。其源代码如下程序所示。
package behavior.memento;
/**
* @author Minggg
* 使用备忘录的类
*/
public class ClassA {
private String value;
public ClassA(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Memento createMemento() {
return new Memento(value);
}
public void restoreMemento(Memento memento) {
this.value = memento.getValue();
}
}
(3) 备忘录存储类ClassB.java用来存储 ClassA.java产生的备忘录,因此它拥有一个备忘录对象 memento,并提供getter/setter函数来修改它,其源代码如下程序所示。
package behavior.memento;
/**
* @author Minggg
* 备忘录存储类
*/
public class ClassB {
private Memento memento;
public ClassB (Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this memento = memento;
}
}
为了进行测试,首先需要创建一个原始类 ClassA的对象classa,然后根据 classa 取得一个备忘录对象存储在 CassB 的对象 classb中,然后在需要的时候可以从 classb 中取出备忘录对象恢复到以前的状态。其源代码如下程序所示。
package behavior.memento;
public class Test {
public static void main(String[] args){
ClassA classa = new ClassA("小张");
//创建备忘录
ClassB classb = new ClassB(classa.createMemento());
// 修改白身状态
classa.setValue("小李");
// 回复备忘录
classa.restoreMemento(classb.getMemento());
}
}
二、何时使用备忘录模式
从上面的讨论可以看出,使用了备忘录模式来实现保存对象的历史状态,可以有效地保存内存对象数据,把内部信息对其他对象屏蔽起来,从而保持了封装。但是如果备份的目标对象存在大量的信息,或者创建、恢复操作非常频繁,则可能造成很大的开销。因此,使用备忘录模式的前提:必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
简单的说,一个对象在执行一系列操作的过程中,可能会为了防止因操作失败而致使前面所做的一切白费或是其他原因,需要对执行过程中的状态进行保存。然而该复杂对象的状态中的内容又不希望被除它之外的任何对象修改(即使该状态可能会保存在外部对象中),这个时候就需要使用备忘录模式。
三、Java中的应用–Session 和 Application 对象
进行对象的缓存在 JavaEE中的应用也很多,比如各种数据缓存方案,包括Memcached、OSChache和 Ehcache 缓存引擎等。除此之外,在基于 Java Web 的 JSP/Servlet 开发中也很常见,比如JSP中的 Application、Session 等,它们就是使用了备忘录模式,用来缓存一个数据对象。
1、Session 会话对象
Session 对象是一个JSP内置对象,它在第一个JSP页面被装载时自动创建,完成会话期管理从一个客户打开浏览器并连接到服务器开始,到客户关闭浏览器离开这个服务器结束,被称为一个会话。当一个客户访问一个服务器时,可能会在这个服务器的几个页面之间切换,服务器应当通过某种办法知道这是一个客户,这就需要Session对象。
当一个客户首次访问服务器上的一个JSP页面时,JSP引擎产生一个 Session 对象,同时分配一个 String 类型的ID号,JSP 引擎同时将这换个ID号发送到客户端,存放在Cookie 中,直到客户关闭浏览器后,服务器端才将该客户的 Session 对象取消,并且与客户的会话对应关系消失。当客户重新打开浏览器再连接到该服务器时,服务器为该客户再创建一个新的Session对象。
Session 对象通过下面的方法来实现对象的缓存。
- public String getld():获取 Session 对象编号。
- public void setAttribute(String key,Object obj):将参数 Object 指定的对象 obj添加到 Session对象中,并为添加的对象指定一个索引关键字。
- public Object getAttribute(String key):获取 Session 对象中含有关键字的对象。
- public Boolean isNew():判断是否是一个新的客户。
2、Application 应用程序对象
服务器启动后就产生了这个 Application对象,当客户在所访问的网站的各个页面之间浏览时,这个 Application对象都是同一个,直到服务器关闭。但是与Session 对象不同的是,所有客户的Application 对象都是同一个,即所有客户共享这个内置的 Application 对象。
Application对象的常用方法如下。
- setAttribute(String key,Object obj):将参数 Object 指定的对象 obj添加到 Application 对象中并为添加的对象指定一个索引关键字。
- getAttribute(String key):获取 Application 对象中含有关键字的对象