CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / DESIGN_PATTERNS / P-115 · 设计模式

设计模式 021:备忘录(Memento)

备忘录模式(Memento pattern):当你需要让对象返回之前的状态时(例如,你的用户请求”撤销”),你使用备忘录模式。它在不破坏封装的前提下,把对象的内部状态保存到备忘录中,需要时再恢复。

意图

在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。

类图

  • Originator:原始对象
  • Caretaker:负责保存好备忘录
  • Memento:备忘录,存储原始对象的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。

原文图片丢失,据文字重绘。

classDiagram
    class Calculator {
        <<interface>>
        +backupLastCalculation() PreviousCalculationToCareTaker
        +restorePreviousCalculation(PreviousCalculationToCareTaker)
        +getCalculationResult() int
        +setFirstNumber(int)
        +setSecondNumber(int)
    }
    class CalculatorImp {
        -int firstNumber
        -int secondNumber
    }
    class PreviousCalculationToOriginator {
        <<interface>>
        +getFirstNumber() int
        +getSecondNumber() int
    }
    class PreviousCalculationToCareTaker {
        <<interface>>
    }
    class PreviousCalculationImp {
        -int firstNumber
        -int secondNumber
    }
    class Client
    Calculator <|.. CalculatorImp
    PreviousCalculationToOriginator <|.. PreviousCalculationImp
    PreviousCalculationToCareTaker <|.. PreviousCalculationImp
    CalculatorImp ..> PreviousCalculationImp : 创建/恢复
    Client ..> CalculatorImp
    Client --> PreviousCalculationToCareTaker : 只持有窄接口

实现

以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复(实现参考:Memento Pattern - Calculator Example - Java Sourcecode)。

Originator 接口与实现:

/**
 * 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;
    }
}

备忘录对象的两个接口:宽接口给 Originator,窄接口给 Caretaker:

/**
 * 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
 *
 * 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 只通过窄接口保存备忘录:

/**
 * 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

第一次计算 10 + 100 = 110;输入被误改为 17 和 -290 后得到 -273;恢复备忘录保存的状态后,结果回到 110。

JDK 中的应用

  • java.io.Serializable

系列导航

← Angular 22+ 教程 21:Signals——响应式编程新范式 目录 JHipster 开发 21:多环境配置管理 →
← 返回文章列表