1、核心
就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。
2、结构
—源发器类Originator:负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
—备忘录类Memento:负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
—负责人类CareTaker:负责备忘录Memento,不能对Memento的内容进行访问或者操作。
3、应用场景
—棋类游戏中的悔棋
—普通软件中的撤销操作
—数据库软件中的事务管理中的回滚操作
—Photoshop软件中的历史记录
4、代码示例
问题:雇员状态保存
/**
* 源发器类:雇员类
* @author ly1
*
*/
public class Emp {
private String name;
private int age;
private double salary;
public Emp(String name, int age, double salary) {
super();
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
/**
* 创建备忘录
* @return 新创建的备忘录对象
*/
public EmpMemento memento(){
return new EmpMemento(this);
}
/**
* 用于恢复自身状态
* @param em
*/
public void recovery(EmpMemento em){
this.name = em.getName();
this.age = em.getAge();
this.salary = em.getSalary();
}
}
/**
* 备忘录类
* @author ly1
*
*/
public class EmpMemento {
private String name;
private int age;
private double salary;
public EmpMemento(Emp emp) {
super();
this.name = emp.getName();
this.age = emp.getAge();
this.salary = emp.getSalary();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
import java.util.Stack;
/**
* 负责人类
* @author ly1
*
*/
public class CareTaker {
/**
* 存储备忘信息,先进后出
*/
private Stack<EmpMemento> stack = new Stack<EmpMemento>();
/**
* 压入备忘点
* @param em
*/
public void memento(EmpMemento em){
stack.push(em);
}
/**
* 弹出最近的备忘点
* @return 最近的备忘录对象
*/
public EmpMemento getMemento(){
return stack.pop();
}
}
/**
* 客户端调用
* @author ly1
*
*/
public class Client {
public static void main(String[] args) {
//创建负责人类,管理备忘录信息
CareTaker taker = new CareTaker();
Emp emp = new Emp("张三", 22, 10000);
System.out.println(emp.getName()+"---"+emp.getAge()+"---"+emp.getSalary());
taker.memento(emp.memento()); //创建备忘点
emp.setAge(123);
System.out.println(emp.getName()+"---"+emp.getAge()+"---"+emp.getSalary());
taker.memento(emp.memento()); //创建备忘点
emp.setName("李四");
System.out.println(emp.getName()+"---"+emp.getAge()+"---"+emp.getSalary());
taker.memento(emp.memento()); //创建备忘点
System.out.println("开始撤销------------------------------");
emp.recovery(taker.getMemento());
System.out.println(emp.getName()+"---"+emp.getAge()+"---"+emp.getSalary());
emp.recovery(taker.getMemento());
System.out.println(emp.getName()+"---"+emp.getAge()+"---"+emp.getSalary());
emp.recovery(taker.getMemento());
System.out.println(emp.getName()+"---"+emp.getAge()+"---"+emp.getSalary());
}
}
结果:
张三---22---10000.0
张三---123---10000.0
李四---123---10000.0
开始撤销------------------------------
李四---123---10000.0
张三---123---10000.0
张三---22---10000.0
5、优点
- 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时,
使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。- 本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需
要的这些状态的版本。- 当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
6.缺点
- 如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
- 当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很昂贵。
- 当发起人角色的状态改变的时候,有可能这个协议无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。