备忘录模式

118 阅读3分钟

备忘录模式的原理

备忘录模式,也叫快照(Snapshot)模式,英文翻译是 Memento Design Pattern。在 GoF 的《设计模式》一书中,备忘录模式是这么定义的:

Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation.

翻译成中文就是:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

需求

游戏角色有攻击力和防御力,在大战boss前,大战两分钟,四分钟,保存自身的状态,当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前状态

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

  • 浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面

  • 数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当前已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中

  • 编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键 Ctrl + z 撤销,撤销后可以按 Ctrl + y 重做

  • 虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子

  • Git版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把它们自动串成一条时间线,每个版本都有一个版本号,使用 git reset --hard 版本号 即可回到指定的版本,让代码时空穿梭回到过去某个历史时刻

  • 棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下

  1. originator: 对象(需要保存状态的对象)
  2. Memento: 备忘录对象,负责保存好记录,即originator的状态
  3. Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
package com.evan.memento;
import lombok.Data;

import	java.util.HashMap;



import java.util.ArrayList;

/**
 * 这是一个守护者对象,保存游戏角色的状态
 */
@Data
public class Caretaker {

    //如果只保存一次状态可以使用
    private Memento memento;

    // 保存GameRole多个状态
    private ArrayList<Memento> mementos;

    // 保存多个GameRole多个状态
    private HashMap<String, ArrayList<Memento>> rolesMementos;


}

package com.evan.memento;

import lombok.Data;

@Data
public class GameRole {
    // 攻击力
    private int vit;
    // 防御力
    private int def;

    // 创建Memento 即根据当前的状态得到Memento
    public Memento createMemento() {
        return new Memento(vit, def);
    }

    // 从备忘录中得到GameRole的状态
    public void recoverGameRoleMemento(Memento memento) {
        this.vit = memento.getVit();
        this.def = memento.getDef();
    }


    public void display() {
        System.out.println("游戏角色当前的攻击力:" + this.vit + "防御力" + this.def);
    }


}

package com.evan.memento;


import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Memento {
    // 攻击力
    private int vit;
    // 防御力
    private int def;
}


package com.evan.memento;

import java.util.ArrayList;
import java.util.Random;

public class Client {
    public static void main(String[] args) {
        // 创建游戏角色
        GameRole gr = new GameRole();
        gr.setDef(100);
        gr.setVit(100);


        System.out.println("和boss 大战前的状态");
        gr.display();

        // 把当前状态保存到Caretaker对象种
        Caretaker caretaker = new Caretaker();
        ArrayList<Memento> mementos = new ArrayList();
        mementos.add(gr.createMemento());

        System.out.println("和boss 大战两分钟");
        gr.setDef(70);
        gr.setVit(70);

        mementos.add(gr.createMemento());
        gr.display();


        System.out.println("和boss 大战四分钟");
        gr.setDef(50);
        gr.setVit(50);
        mementos.add(gr.createMemento());
        gr.display();
        caretaker.setMementos(mementos);

        System.out.println("和boss 大战后,随机恢复");
        Random  r = new Random ();
        int i = r.nextInt(3);

        gr.recoverGameRoleMemento(caretaker.getMementos().get(i));
        System.out.println("恢复后的状态");
        gr.display();

    }
}