顿搜
备忘录模式(Memento)——23种设计模式之行为型模式
在软件构建过程中,某些对象的状态在转换过 程中,可能由于某种需要,要求程序能够回溯 到对象之前处于某个点时的状态。如果使用一 些公用接口来让其他对象得到对象的状态,便 会暴露对象的细节实现。如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性?
在不破坏封装性的前提下,捕获一个对象的内部状态, 并在该对象之外保存这个状态。这样以后就可将该对 象恢复到原先保存的状态。
一个memento是一个对象, 它存储另一个对象在某个瞬间的内部状态,而后者称为备忘录的原发器 (originator)。当需要设置原发器的检查点(checkpoint) 时, 取消操作机制会向原发器请求一个备忘录。原发器用描述当前状态的信息初始化该备忘录。只有原发器可以向备忘录中存取信息,备忘录对其他的对象 “不可见”。
一、类图表示
备忘录模式涉及Memento(备忘录),Originator(原发器),Caretaker(管理器)等角色
- ①、Originator(原发器):创建一个备忘录,用以记录当前时刻它的内部状态,使用备忘录恢复内部状态。
- ②、Caretaker(管理器):负责保存好备忘录,不能对备忘录的内容进行操作或检查
- ③、Memento(备忘录):备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。防止原发器以外的其他对象访问备忘录。备忘录实际上有两 个接口,管理者(caretaker) 只能看到备忘录的窄接口—它只 能将备忘录传递给其他对象。相反, 原发器能够看到一个宽接口, 允许它访问返回到先前状态所需的所有数据。理想的 情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态。
二、代码示例
public class Originator {
private String state;
/**
* 工厂方法,返回一个新的备忘录对象
*/
public Memento createMemento(){
return new Memento(state);
}
/**
* 将发起人恢复到备忘录对象所记载的状态
*/
public void restoreMemento(Memento memento){
this.state = memento.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.println("当前状态:" + this.state);
}
}public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}public class Caretaker {
private Memento memento;
/**
* 备忘录的取值方法
*/
public Memento retrieveMemento(){
return this.memento;
}
/**
* 备忘录的赋值方法
*/
public void saveMemento(Memento memento){
this.memento = memento;
}
}public class Client {
public static void main(String[] args) {
Originator o = new Originator();
Caretaker c = new Caretaker();
//改变负责人对象的状态
o.setState("On");
//创建备忘录对象,并将发起人对象的状态储存起来
c.saveMemento(o.createMemento());
//修改发起人的状态
o.setState("Off");
//恢复发起人对象的状态
o.restoreMemento(c.retrieveMemento());
System.out.println(o.getState());
}
}三、模式分析
为了实现对备忘录对象的封装,需要对备忘录的调用进行控制,
- ①、对于原发器而言,它可以调用备忘录的所有信息,允许原发器访问返回到先前状态所需的所有数据;
- ②、对于管理器而言,只负责备忘录的保存并将备忘录传递给其他对象;
- ③、对于其他对象而言,只需要从管理器处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。
- ④、理想的情况是只允许生成该备忘录的那个原发器访问 备忘录的内部状态。
四、优缺点分析
1、优点
- ①、保持封装边界,该模式把可能很复杂的 Originator内部信息对其他对象屏蔽起来
- ②、它简化了Originator,让客户管理它们请求的状态将会简化Originator,并且使得客户工作结束时无需通知Originator
2、缺点
- ①、使用备忘录可能代价很高
- ②、定义窄接口和宽接口:有些语言可能难以保证只有原发器可以访问备忘录的状态
五、模式间关系
1、与命令模式的关系
命令可使用备忘录来为可撤销的操作维护状态
2、与迭代器模式的关系
当备忘录模式支持多个checkpoints时, 在各个checkpoints之间进行遍历可用迭代模式
六、适用场景
由原发器管理, 却又必须存储在原发器之外的信息
- ①、必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态
- ②、如果一个用接口来让其它对象直接得到这些状 态,将会暴露对象的实现细节并破坏对象的封装性
- ③、数据库管理系统DBMS所提供的事务管理应用了备忘录模式。当数据库某事务中一条数据操作语句执行失败时,整 个事务将进行回滚操作,系统回到事务执行之前的状态。
七、总结
在不破坏封装性的前提下,捕获一个对象的内部状态, 并在该对象之外保存这个状态。
- ①、备忘录是被动的。只有创建备忘录的原发器会对它的状态进行赋值和检索
- ②、一个memento是一个对象, 它存储另一个对象在某个瞬间的内部状态,而后者称为备忘录的原发器 (originator)。
- ③、当需要设置原发器的检查点(checkpoint)时,,取消操作机制会向原发器请求一个备忘录。原发器用描述当前状态的信息初始化该备忘录。
- ④、只有原发器可以向备忘录中存取信息,备忘录对其他的对象“不可见”。
- ⑤、在实现备忘录模式时,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口:一个为原 发器使用的宽接口;一个为其他对象使用的窄接口。
- ⑥、在实现备忘录模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改进备忘录模式。
