备忘录模式属于行为型模式,其意图是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将对象恢复到原先保存的状态。有时有必要记录一个对象的内部状态。为了允许用户取消不确定的操作,或从错误中恢复过来,需要实现检查点和取消机制,而要实现这些机制,你必须事先将状态信息保存在某处,这样才能将对象恢复到他们先前的状态。但是对象通常封装了其部分或所有的状态信息,使得其状态不能被其他的对象访问。也就不可能在该对象之外保存其状态。而暴露其内部状态又将违反封装的原则,可能有损应用的可靠性和可扩展性。
协作关系是:客户端每操作一步Employee(原发器)的信息,便产生一个Memento对象,并把Memento对象发送给CareTaker保存,当需要恢复的时候便从CareTaker对象中取出Memento对象,进行状态的恢复。
参与者:
Memento(备忘录 Memento):
备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。防止原发器之外的其他对象访问备忘录,备忘录实际上有两个接口,管理者CareTaker只能看到备忘录的窄接口--它只能将备忘录传递给其他对象。相反,原生器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只能允许生成备忘录的那个原发器访问本备忘录的内部状态。
Originator(原发器,如Employee):
原发器创建一个备忘录,用以记录当前时刻它的内部状态。使用备忘录恢复内部的状态。
CareTaker(负责人 ):
负责保存好备忘录。不能对备忘录的内容进行操作或检查。
效果:
备忘录模式有以下的一些效果:
1) 保持封装的边界: 使用备忘录可以避免暴露一些只应有原发器管理却又必须存储在原发器之外的信息。该模式把可能很复杂的Originator内部信息对其他对象屏蔽起来,从而保持了封装的边界。
2) 它简化了原发器 在其他的保持封装性的设计中,originator负责保持客户请求过的内部状态版本。这就把所有存储管理的重任务交给了Originator。让客户管理他们请求的状态将会简化Originator,并且使得客户工作结束时无需通知原发器。
3) 使用备忘录可能代价很高 如果原发器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。除非封装和恢复Originator状态的开销不大,否则该模式可能并不适合。
4) 定义窄接口和宽接口 在一些语言中可能难以保证只有原发器可访问备忘录的状态。
5) 维护备忘录的潜在代价 管理器负责删除它所维护的备忘录。然而 ,管理器不知道备忘录中有多少个状态,因此当存储备忘录时,一个本来很小的管理器,可能会产生大量的存储开销。
相关的代码:
Memento代码:
package memento;
public class Memento{
String name;
int age;
public Memento(String name,int age){
this.name = name;
this.age = age;
}
}
Employee模式:
package memento;
public class Employee{
private String name;
private int age;
public Employee(String aName,int aAge){
name = aName;
age = aAge;
}
public void setName(String aName){
name = aName;
}
public void setAge(int aAge){
age = aAge;
}
public Memento saveMemento(){
return new Memento(name,age);
}
public void restoreMemento(Memento memento){
age = memento.age;
name = memento.name;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
}
CareTaker代码:
package memento;
import java.util.Vector;
public class CareTaker{
private Vector v;
private int current;
public CareTaker(){
current = -1;
v = new Vector();
}
public void setMemento(Memento mem){
for(int i = current+1;i<v.size();i++)
v.remove(i);
current ++;
v.add(mem);
}
public Memento getMemento(){
if(current>0){
current --;
return(Memento) v.get(current);
}
return null;
}
}
Client代码:
package memento;
public class Client{
public static void show(Employee e){
System.out.println("-----------------------------------");
System.out.println("Name:"+e.getName());
System.out.println("Age:" + e.getAge());
System.out.println("-----------------------------------");
}
public static void main(String[] args){
Employee e = new Employee("lili",25);
CareTaker ct = new CareTaker();
show(e);
ct.setMemento(e.saveMemento());
e.setName("litianli");
show(e);
ct.setMemento(e.saveMemento());
e.setAge(45);
show(e);
ct.setMemento(e.saveMemento());
//restore
e.restoreMemento(ct.getMemento());
show(e);
e.restoreMemento(ct.getMemento());
show(e);
}
}
这里使用的是宽接口模式,Memento对象对外(指Employee之外的对象)是完全暴露的,其他对象可以随意的访问、修改、创建这个对象,没有达到完全封装Employee的状态
我们希望,Memento对象,对Employee之外的对象是完全隐身的,无法操作这个对象,只有employee可以创建,修改,访问。这样employee对象的状态才会安全。
解决的方法是使用窄接口,这种方法,memento对象提供的方法,只有employee可以访问。因为memento是employee的内部类。
1 public interface NarrowMemento {
2 public void narrowMethod();
3 }
4 class Originator {
5 private String state;
6 private NarrowMemento memento;
7 public Originator() {
8 }
9 public NarrowMemento createMemento() {
10 memento = new Memento(this.state);
11 return memento;
12 }
13 public void restoreMemento(NarrowMemento memento) {
14 Memento aMemento = (Memento)memento;
15 this.setState(aMemento.getState());
16 }
17 public String getState() {
18 return this.state;
19 }
20 public void setState(String state) {
21 this.state = state;
22 }
23 //内部类
24 protected class Memento implements NarrowMemento {
25 private String savedState;
26 private Memento(String someState) {
27 saveState = someState;
28 }
29 private void setState(String someState) {
30 saveState = someState;
31 }
32 private String getState() {
33 return saveState;
34 }
35 public void narrowMethod() {
36 System.out.println("this is narrow method");
37 }
38
39 }
40 public NarrowMemento getNarrowMemento() {
41 return memento;
42 }
43 }
44 public class Caretaker {
45 private NarrowMemento memento;
46 public NarrowMemento retrieveMemento() {
47 return this.memento;
48 }
49 public void saveMemento(NarrowMemento memento) {
50 this.memento = memento;
51 }
52 }
53 public class Client {
54 private static Originator o = new Originator();
55 private static Caretaker c = new Caretaker();
56 public static void main(String[] args) {
57 //use wide interface
58 o.setState("On");
59 c.saveMemento(o.createMemento());
60 o.setState("Off");
61 o.restoreMemento(c.retrieveMemento());
62 //use narrow interface
63 NarrowMemento memento = o.getNarrowMemento();
64 memento.narrowMethod();
65
66 }
67 }