Java设计模式(二十一):备忘录设计模式

1. 应用场景

备忘录模式经常可以遇到,譬如下面这些场景:

  • 浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面
  • 数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当前已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中
  • 编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键 Ctrl + z 撤销,撤销后可以按 Ctrl + y 重做
  • 虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子
  • Git版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把它们自动串成一条时间线,每个版本都有一个版本号,使用 git reset --hard 版本号 即可回到指定的版本,让代码时空穿梭回到过去某个历史时刻
  • 棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下

2. 概念

在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。它是一种对象行为型模式,其别名为Token。

3. Class Diagram

Java设计模式(二十一):备忘录设计模式_第1张图片

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

4. 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
 * 

* 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

5. 优点和缺点

5.1 优点

  • 将被存储的状态放在外面,不要和关键对象混在一起,这样可以帮助维护内聚。
  • 保持关键对象的数据封装
  • 提供了容易实现的恢复能力

5.2 用途和缺点

  • 备忘录用于存储状态。
  • 使用备忘录的缺点:存储和恢复的过程可能相对耗时。
  • 在Java系统中,其实可以考虑用序列化(serializaiton)机制存储系统的状态。

6. JDK

  • java.io.Serializable

你可能感兴趣的:(Java设计模式,Java设计模式)