Intent
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
应用实例
- 后悔药。
- 打游戏时的存档。
- Windows 里的 ctri + z。
- 数据库的事务管理。
使用场景
- 需要保存/恢复数据的相关状态场景。
- 提供一个可回滚的操作。
优点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点
- 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
Class Diagram
Originator(生成者)
Originator
角色会在保存自己的最新状态时生成Memento
角色。当把以前保存的Memento
角色传递给Originator
角色时,它会将自己恢复至生成该Memento
角色时的状态。
Memento(纪念品)
Memento
角色会将Originator
角色的内部信息整合在一起。在Memento
角色中虽然保存了Originator
角色的信息,但它不会向外公布这些信息。
Memento
角色有以下两种接口(API)。
-
wide interface
——宽接口(API)
Memento
角色提供的“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento
角色的内部信息,因此能够使用宽接口(API)的只有Originator
角色。 -
narrow interface
——窄接口(API)
Memento
角色为外部的Caretaker
角色提供了“窄接口(API)”。可以通过窄接口(API)获取的Memento
角色的内部信息非常有限,因此可以有效地防止信息泄漏。
通过对外提供以上两种接口(API),可以有效地防止对象的封装性被破坏。
Caretaker(负责人)
当Caretaker
角色想要保存当前的Originator
角色的状态时,会通知Originator
角色。Originator
角色在接收到通知后会生成Memento
角色的实例并将其返回给Caretaker
角色。由于以后可能会用Memento
实例来将Originator
恢复至原来的状态,因此Caretaker
角色会一直保存Memento
实例。在示例程序中,由Main类扮演此角色。
不过,Caretaker
角色只能使用Memento
角色两种接口(API)中的窄接口(API),也就是说它无法访问Memento
角色内部的所有信息。它只是将Originator
角色生成的Memento
角色当作一个黑盒子保存起来。
虽然Originator
角色和Memento
角色之间是强关联关系,但Caretaker
角色和Memento
角色之间是弱关联关系。Memento
角色对Caretaker
角色隐藏了自身的内部信息。
Implementation
以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。
/**
* Originator Interface
*/
public interface Calculator {
// Create Memento
PreviousCalculationToCareTaker backupLastCalculation();
// setMemento
void restorePreviousCalculation(PreviousCalculationToCareTaker memento);
int getCalculationResult();
void setFirstNumber(int firstNumber);
void setSecondNumber(int secondNumber);
}
/**
* Originator Implementation
*/
public class CalculatorImp implements Calculator {
private int firstNumber;
private int secondNumber;
@Override
public PreviousCalculationToCareTaker backupLastCalculation() {
// create a memento object used for restoring two numbers
return new PreviousCalculationImp(firstNumber, secondNumber);
}
@Override
public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) {
this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber();
this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber();
}
@Override
public int getCalculationResult() {
// result is adding two numbers
return firstNumber + secondNumber;
}
@Override
public void setFirstNumber(int firstNumber) {
this.firstNumber = firstNumber;
}
@Override
public void setSecondNumber(int secondNumber) {
this.secondNumber = secondNumber;
}
}
/**
* Memento Interface to Originator
*
* This interface allows the originator to restore its state
*/
public interface PreviousCalculationToOriginator {
int getFirstNumber();
int getSecondNumber();
}
/**
* Memento interface to CalculatorOperator (Caretaker)
*/
public interface PreviousCalculationToCareTaker {
// no operations permitted for the caretaker
}
/**
* Memento Object Implementation
* <p>
* Note that this object implements both interfaces to Originator and CareTaker
*/
public class PreviousCalculationImp implements PreviousCalculationToCareTaker,
PreviousCalculationToOriginator {
private int firstNumber;
private int secondNumber;
public PreviousCalculationImp(int firstNumber, int secondNumber) {
this.firstNumber = firstNumber;
this.secondNumber = secondNumber;
}
@Override
public int getFirstNumber() {
return firstNumber;
}
@Override
public int getSecondNumber() {
return secondNumber;
}
}
/**
* CareTaker object
*/
public class Client {
public static void main(String[] args) {
// program starts
Calculator calculator = new CalculatorImp();
// assume user enters two numbers
calculator.setFirstNumber(10);
calculator.setSecondNumber(100);
// find result
System.out.println(calculator.getCalculationResult());
// Store result of this calculation in case of error
PreviousCalculationToCareTaker memento = calculator.backupLastCalculation();
// user enters a number
calculator.setFirstNumber(17);
// user enters a wrong second number and calculates result
calculator.setSecondNumber(-290);
// calculate result
System.out.println(calculator.getCalculationResult());
// user hits CTRL + Z to undo last operation and see last result
calculator.restorePreviousCalculation(memento);
// result restored
System.out.println(calculator.getCalculationResult());
}
}
输出结果
110
-273
110