定义(源于GoF《设计模式》):在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
涉及角色: 1.Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。 2.Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。 3.Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。 备忘录模式的优点和缺点 一、备忘录模式的优点 1、有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时, 使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。 2、本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需 要的这些状态的版本。 3、当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。 二、备忘录模式的缺点:1、如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
2、当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很昂贵。
3、当发起人角色的状态改变的时候,有可能这个协议无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。
备忘录模式需要对发起者提供宽接口,对管理者提供窄接口,这样才能提高整个设计的封装性
下面给出 3中解决方法
1.
下面是关于此解决方案的一个例子
package 备忘录模式;
public class Originate {
CareTaker careTaker;
int state;
public Originate(int state){
this.state=state;
}
public void setState(int state){
this.state=state;
}
public int getState(){
return state;
}
public void setCareTaker(CareTaker careTaker){
this.careTaker=careTaker;
}
public CareTaker getCareTaker(){
return careTaker;
}
//下面是恢复状态
public void getMemento(){
Memento mem =careTaker.getMemento();
state =mem.getState();
}
//下面是保存状态
public void creteMemento(){
Memento mem=new Memento();
mem.setState(state);
careTaker.setMemento(mem);
}
class Memento{
private int state;
private int getState(){
return state;
}
private void setState(int state){
this.state=state;
}
}
}
package 备忘录模式;
import 备忘录模式.Originate.Memento;
public class CareTaker {
Originate.Memento mem;
public void setMemento(Originate.Memento mem){
this.mem=mem;
}
public Originate.Memento getMemento(){
return mem;
}
}
package 备忘录模式;
public class Client {
public static void main(String [] args){
Originate originate=new Originate(90);
CareTaker careTaker =new CareTaker();
Originate.Memento mem=originate.new Memento();
originate .setCareTaker(careTaker);
careTaker.setMemento(mem);
System.out.println("状态修改前");
System.out.println(originate.getState());
originate.creteMemento();
System.out.println("状态修改后");
originate.setState(80);
System.out.println(originate.getState());
System.out.println("状态恢复后");
originate.getMemento();
System.out.println(originate.getState());
}
}
状态修改前
90
状态修改后
80
状态恢复后
90
第三种方法
第三种方式是不太推荐使用的:使用clone方法来简化备忘录模式。由于Java提供了clone机制,这使得复制一个对象变得轻松起来。使用了clone机制的备忘录模式,备忘录角色基本可以省略了,而且可以很好的保持对象的封装。但是在为你的类实现clone方法时要慎重啊。
在上面的教学代码中,我们简单的模拟了备忘录模式的整个流程。在实际应用中,我们往往需要保存大量“备忘发起角色”的历史状态。这时就要对我们的“备忘录管理者角色”进行改造,最简单的方式就是采用容器来按照顺序存放备忘录角色。这样就可以很好的实现undo、redo功能了。
从上面的讨论可以看出,使用了备忘录模式来实现保存对象的历史状态可以有效地保持封装边界。使用备忘录可以避免暴露一些只应由“备忘发起角色”管理却又必须存储在“备忘发起角色”之外的信息。把“备忘发起角色”内部信息对其他对象屏蔽起来, 从而保持了封装边界。
但是如果备份的“备忘发起角色”存在大量的信息或者创建、恢复操作非常频繁,则可能造成很大的开销。
GOF在《设计模式》中总结了使用备忘录模式的前提:
1) 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
2) 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。