Update 设计模式 - 备忘录.md

重新绘制备忘录类图;补充类图与示例代码中宽窄接口的说明,精简汉化代码注释;更新 JDK 应用中的 StateEdit 案例。
This commit is contained in:
CyC2018
2026-10-30 20:19:43 +08:00
parent 0733989adb
commit 6873ed2617
2 changed files with 24 additions and 33 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 291 KiB

+24 -33
View File
@@ -4,27 +4,31 @@
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到之前保存的状态。
### 类图
- Originator:原始对象
- Caretaker:负责保存好备忘录
- Memento:备忘录,存储原始对象的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。
- Originator:原始对象,负责创建备忘录和用备忘录恢复状态;
- Caretaker:负责保存好备忘录;
- Memento:备忘录,存储原始对象的状态。
备忘录对外提供两个接口:提供给 Caretaker 的窄接口,只能保存和传递备忘录,看不到其中的数据;提供给 Originator 的宽接口,可以读出恢复状态所需的所有数据。这样 Caretaker 保管备忘录时不依赖其中的数据,也不会破坏封装。
<div align="center"> <img src="pics/dp-memento.png" width="760px"/> </div><br>
### 实现
以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。
代码中的角色:CalculatorImp 是 Originator;PreviousCalculationToCareTaker 是窄接口,没有任何方法;PreviousCalculationToOriginator 是宽接口;PreviousCalculationImp 同时实现这两个接口;Client 是 Caretaker,它只拿到窄接口类型的备忘录。需要注意的是,这里的两个接口都是 public 的,Client 强转后仍能读出数据,隔离只是约定;要真正禁止读取,可以去掉公开的宽接口,把备忘录实现为 Originator 的私有静态嵌套类,只实现窄接口,由 Originator 在内部读取它的字段。
实现参考:[Memento Pattern - Calculator Example - Java Sourcecode](https://www.oodesign.com/memento-pattern-calculator-example-java-sourcecode.html)
```java
/**
* Originator Interface
* Originator 接口
*/
public interface Calculator {
// Create Memento
// 创建备忘录
PreviousCalculationToCareTaker backupLastCalculation();
// setMemento
// 用备忘录恢复状态
void restorePreviousCalculation(PreviousCalculationToCareTaker memento);
int getCalculationResult();
@@ -37,7 +41,7 @@ public interface Calculator {
```java
/**
* Originator Implementation
* Originator 实现
*/
public class CalculatorImp implements Calculator {
@@ -46,7 +50,7 @@ public class CalculatorImp implements Calculator {
@Override
public PreviousCalculationToCareTaker backupLastCalculation() {
// create a memento object used for restoring two numbers
// 创建保存两个数的备忘录
return new PreviousCalculationImp(firstNumber, secondNumber);
}
@@ -58,7 +62,7 @@ public class CalculatorImp implements Calculator {
@Override
public int getCalculationResult() {
// result is adding two numbers
// 结果是两个数的和
return firstNumber + secondNumber;
}
@@ -76,9 +80,7 @@ public class CalculatorImp implements Calculator {
```java
/**
* Memento Interface to Originator
*
* This interface allows the originator to restore its state
* 提供给 Originator 的宽接口,用于恢复状态
*/
public interface PreviousCalculationToOriginator {
int getFirstNumber();
@@ -88,18 +90,16 @@ public interface PreviousCalculationToOriginator {
```java
/**
* Memento interface to CalculatorOperator (Caretaker)
* 提供给 Caretaker 的窄接口
*/
public interface PreviousCalculationToCareTaker {
// no operations permitted for the caretaker
// Caretaker 不能进行任何操作
}
```
```java
/**
* Memento Object Implementation
* <p>
* Note that this object implements both interfaces to Originator and CareTaker
* 备忘录实现,同时实现了宽接口和窄接口
*/
public class PreviousCalculationImp implements PreviousCalculationToCareTaker,
PreviousCalculationToOriginator {
@@ -126,37 +126,28 @@ public class PreviousCalculationImp implements PreviousCalculationToCareTaker,
```java
/**
* CareTaker object
* Caretaker
*/
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
// 用户按下 Ctrl + Z,恢复到上一次计算
calculator.restorePreviousCalculation(memento);
// result restored
System.out.println(calculator.getCalculationResult());
}
}
@@ -169,9 +160,9 @@ public class Client {
```
### JDK 中的应用
- java.io.Serializable
javax.swing.undo.StateEdit 用到了同样的思路:被编辑的对象实现 StateEditable,修改前后由对象把自己的状态写入 Hashtable,撤销或重做时再用它恢复。序列化可以用来把备忘录保存到文件中,但 Serializable 只是一个标记接口,本身不构成备忘录。
## 参考资料
- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007.
- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.
- Gamma E. 设计模式:可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.