转自http://www.kongzid.com/archives/design18
备忘录模式(Memento)的定义
备忘录模式又叫做快照模式,指在不破坏封装性的前提下,获取到一个对象的内部状态,并在对象之外记录或保存这个状态。在有需要的时候可将该对象恢复到原先保存的状态。我们相当于把对象原始状态备份保留,所以叫备忘录模式。
备忘录模式能记录一个对象的内部状态,当用户后悔时能撤销当前操作,使数据恢复到它原先的状态。其实很多应用软件都提供了这项功能,如 Word、记事本、Photoshop、Eclipse 等软件在编辑时按 Ctrl+Z 组合键时能撤销当前操作,使文档恢复到之前的状态;还有在 IE 中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。
备忘录模式(Memento)优缺点
备忘录模式是一种对象行为型设计模式,其主要优点如下:
- 备忘录模式可以把发起人内部信息对象屏蔽起来,从而可以保持封装的边界。实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
- 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
- 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。(当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。)
缺点:
- 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
- 当发起者对象的状态改变的时候,有可能这个协议无效。如果状态改变的成功率达不到要求,可以考虑采取“假如”协议模式。
适用环境:
- 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
- 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
备忘录模式(Memento)的结构
备忘录模式的核心是设计备忘录类以及用于管理备忘录的管理者类,主要角色如下。
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。Originator可根据需要决定Memento存储Originator的哪些内部状态。
- 备忘录(Memento)角色:负责存储发起人的内部状态,并可防止Originator以外的其他对象访问备忘录Memento,在需要的时候提供这些内部状态给发起人。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问回到先前状态所需的所有数据。
- 管理者/负责人(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行修改或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
备忘录模式(Memento)的应用实例
备忘录在jsp+javabean的使用:在一系统中新增帐户时,在表单中需要填写用户名、密码、联系电话、地址等信息,如果有些字段没有填写或填写错误,当用户点击“提交”按钮时,需要在新增页面上保存用户输入的选项,并提示出错的选项。这就是利用JavaBean的scope="request"或scope="session"特性实现的,即是用备忘录模式实现的。
发起人代码(创建一个含有当前的内部状态的备忘录对象;使用备忘录对象存储其内部状态。)
public class Person {
private String name;
private String sex;
private int age;
// GETTER/SETTER方法省略
public Person(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public Person() {}
public void display() {
System.out.println(" name "+name+" sex "+sex+" age "+age);
}
//创建一个备份
public Memento createMemento(){
return new Memento(name, sex, age);
}
//恢复一个备份
public void restoreMemento(Memento memento) {
this.name=memento.getName();
this.sex = memento.getSex();
this.age = memento.getAge();
}
}
备忘录代码(将发起人对象的内部状态存储起来;可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。)
public class Memento {
private String name;
private String sex;
private int age;
public Memento(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public Memento() {
super();
}
// getter、setter略
}
管理者代码(负责保存备忘录对象;不保存备忘录对象的内容。)
public class Caretaker {
private Memento memento;
// 如果需要存储多个备份,可以使用集合
//private List<Memento> list = new ArrayList<Memento>();
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
测试代码
public class Mainclass {
public static void main(String[] args) {
Person person = new Person("zhangsan","nan",20);
//Memento memento = person.createMemento();
Caretaker caretaker = new Caretaker();
caretaker.setMemento(person.createMemento());
person.display();
person.setName("lisi");
person.setSex("nv");
person.setAge(23);
person.display();
person.restoreMemento(caretaker.getMemento());
person.display();
}
}