描述
定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。又叫Token模式。
类型
对象行为型模式
动机
很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态。
UML类图
时序图
实现
主要角色
- Memento:备忘录
- 存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。
- 防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者只能看到备忘录的窄接口——它只能将备忘录传递给其他对象。相反,原发器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态。
- Originator:原发器
- 创建一个备忘录,用以记录当前时刻它的内部状态。
- 使用备忘录恢复内部状态。
- Caretaker:管理者
- 负责保存好备忘录。
- 不能对备忘录的内容进行操作或检查。
示例
-
Memento:备忘录
public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } }
-
Originator:原发器
public class Originator { private String state; public void setState(String state){ this.state = state; } public String getState(){ return state; } public Memento saveStateToMemento(){ return new Memento(state); } public void getStateFromMemento(Memento Memento){ this.state = Memento.getState(); } }
-
Caretaker:管理者
public class CareTaker { private List<Memento> mementoList = new ArrayList<Memento>(); public void add(Memento state) { mementoList.add(state); } public Memento get(int index) { return mementoList.get(index); } }
-
Client:客户类
public class Client { public static void main(String[] args) { Originator originator = new Originator(); CareTaker careTaker = new CareTaker(); originator.setState("State #1"); originator.setState("State #2"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #3"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #4"); System.out.println("Current State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(0)); System.out.println("First saved State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(1)); System.out.println("Second saved State: " + originator.getState()); } }
适用场景
- 必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
优点
- 保持封装边界。使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。该模式把可能很复杂的Originator内部信息对其他对象屏蔽起来,从而保持了封装边界。
- 简化了原发器。在其他的保持封装性的设计中,Originator负责保持客户请求过的内部状态版本。这就把所有存储管理的重任交给了Originator。
缺点
- 使用备忘录可能代价很高。如果原发器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。
相关模式
- Command:命令可使用备忘录来为可撤消的操作维护状态。
- Iterator:备忘录可用于迭代。基于备忘录的迭代接口有两个优点:1) 在同一个集合上可有多个状态一起工作。2) 它不需要为支持迭代而破坏一个集合的封装性。备忘录仅由集合自身来解释;任何其他对象都不能访问它。