备忘录模式

154 阅读4分钟

这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

备忘录模式

备忘录模式的概念

备忘录(Memento Pattern)模式也被称为快照模式(Snapshot Pattern),备忘录模式主要是指在不破坏封装性的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。备忘录模式在类的创建过程中属于行为型模式。

在软件系统中,备忘录模式可以保存我们提交的一次快照请求后所产生的相关快照,可以通过存储系统各个历史状态的快照,使得我们可以在任一时刻将系统回滚到某一个历史状态。

备忘录模式本质:从发起人实体类(Originator)隔离存储功能,降低实体类的职责。同时由于存储信息(Memento)独立,且存储信息的实体交由管理类(Caretaker)管理,则可以通过为管理类扩展额外的功能对存储信息进行扩展操作(比如增加历史快照功能···)。

备忘录模式的应用场景

备忘录模式在现实中使用的很多,特别是代码中使用

  • Git、SVN的代码回滚等操作

  • 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。

  • 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。

备忘录模式的通用写法

通常情况下备忘录模式一般具有三种角色

  • 发起人(Originator)角色:负责创建一个备忘录,记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。

  • 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人,并且可以防止除了发起人以外的人访问。

  • 备忘录管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

通过玩游戏存档的例子来充分展示备忘录模式

  • Gamer(游戏发起人角色)
public class Gamer {
    Integer coin;
    Integer hp;
    Integer mp;
    Integer level;
    //以上内部状态,需要保存

    GameServer gameServer = new GameServer();

    void saveGameRecord() throws InvocationTargetException, IllegalAccessException {
        System.out.println("正在保存当前记录......");
        GameRecord gameRecord = new GameRecord();
        BeanUtils.copyProperties(gameRecord, this);
        gameServer.add(gameRecord);
    }

    Gamer getFromMemento(Integer id) throws Exception {
        System.out.println("获取历史存档信息。。。。");
        Gamer gameRecord = gameServer.getGameRecord(id);
        return gameRecord;
    }
 }
  • GameRecord(备忘录角色)
public class GameRecord {

    Integer id;
    Integer coin;
    Integer hp;
    Integer mp;
    Integer level;

    /**
     * 获取备忘录信息
     */
    void getCurrent() {
        System.out.println("coin: " + coin + "; \t" + "hp: " + hp + "; \t" + "mp:" + mp);
    }
}
  • GameServer(备忘录管理角色)
public class GameServer {

    /**
     * 管理备忘录信息
     */
    Map<Integer, GameRecord> records = new HashMap<>();
    Integer i = 1;

    void add(GameRecord gameRecord) {
        gameRecord.setId(i++);
        records.put(gameRecord.id, gameRecord);
    }

    Gamer getGameRecord(Integer id) throws Exception {
        GameRecord gameRecord = records.get(id);
        //获取到备忘录里面内容以后还要逆转
        Gamer gamer = new Gamer();
        BeanUtils.copyProperties(gamer, gameRecord);
        return gamer;
    }

}
  • UML结构图

image.png

通过上面的代码和UML结构图可以看到,发起人角色(Gamer)里面存储了很多需要记录和保存的属性,比如coin、level等信息,同时创建了一个备忘录管理者对象(GameServe),通过saveGameRecord方法开保存当前时刻的所有信息,然后通过getFromMemento方法可以获取到之前保存的游戏信息存档。而备忘录角色存储的就是需要存档的游戏信息。备忘录管理者(GameServer)其实就是用来保存每一次发起备忘录的所有记录,通常情况下是以map集合保存的。

备忘录模式的优缺点

优点

  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。

  • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。

  • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

缺点

  • 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。\