Java设计模式(二十二)行为型- 备忘录模式 Memento Pattern(史上最全备忘录模式)与使用场景以及优缺点

  1. 备忘录模式 Memento Pattern

备忘录模式又称为快照模式(Snapshot Pattern)或令牌模式(Token Pattern),是指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

特征:“后悔药”

备忘录模式的主要角色如下:

  • 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

备忘录有两个等效的接口:

  • 窄接口:管理者(Caretaker)对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口(narror Interface),这个窄接口只允许他把备忘录对象传给其他的对象。
  • 宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口(wide Interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

1.1 “ 白箱 ” 备忘录模式

下面就以游戏打怪为简单的例子进行代码实现(下面“黑箱”同这个例子):

备忘录角色对任何对象都提供一个宽接口,备忘录角色的内部所存储的状态就对所有对象公开。

// 游戏角色类
@Data
public class GameRole {
    private Integer vit; // 生命力
    private Integer atk; // 攻击力
    private Integer def; // 防御力
    // 初始化状态
    public void init() {
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }
    // 战斗到0
    public void fight() {
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }
    // 保存角色状态
    public RoleStateMemento saveState() {
        return new RoleStateMemento(this.vit, this.atk, this.def);
    }
    // 回复角色状态
    public void recoverState(RoleStateMemento roleStateMemento) {
        this.vit = roleStateMemento.getVit();
        this.atk = roleStateMemento.getAtk();
        this.def = roleStateMemento.getDef();
    }
    // 展示状态
    public void showState() {
        System.out.println("角色生命力:" + this.vit);
        System.out.println("角色攻击力:" + this.atk);
        System.out.println("角色防御力:" + this.def);
    }
}
// 游戏状态存储类(备忘录类)
@Data
@AllArgsConstructor
public class RoleStateMemento {
    private Integer vit; // 生命力
    private Integer atk; // 攻击力
    private Integer def; // 防御力
}
// 角色状态管理者类
@Data
public class RoleStateCaretaker {
    private RoleStateMemento roleStateMemento;
}
    // 测试结果
    public static void main(String[] args){
      System.out.println("===========打boss前状态===========");
      GameRole gameRole = new GameRole();
      gameRole.init();
      gameRole.showState();
      // 保存进度
      RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
      roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
      System.out.println("===========打boss后状态===========");
      gameRole.fight();
      gameRole.showState();
      System.out.println("===========恢复状态===========");
      gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
      gameRole.showState();
      // ===========打boss前状态===========
      // 角色生命力:100
      // 角色攻击力:100
      // 角色防御力:100
      // ===========打boss后状态===========
      // 角色生命力:0
      // 角色攻击力:0
      // 角色防御力:0
      // ===========恢复状态===========
      // 角色生命力:100
      // 角色攻击力:100
      // 角色防御力:100
    }
 

“白箱”备忘录模式是破坏封装性的,但是通过程序员自律,同样可以在一定程度上实现大部分的用意。

1.2 “ 黑箱 ” 备忘录模式

备忘录角色对发起人对象提供了一个宽接口,而为其他对象提供一个窄接口,在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。

将RoleStateMemento设为GameRole的内部类,从而将RoleStateMemento对象封装在GameRole 里面;在外面提供一个标识接口Memento给RoleStateCaretaker及其他对象使用。这样GameRole类看到的是RoleStateMemento所有的接口,而RoleStateCaretaker及其他对象看到的仅仅是标识接口Memento所暴露出来的接口,从而维护了封装型。

// 窄接口,标识接口
public interface Memento {
}
// 角色状态管理者类
@Data
public class RoleStateCaretaker {
    private Memento memento;
}
// 游戏角色类
@Data
public class GameRole {
    private Integer vit; // 生命力
    private Integer atk; // 攻击力
    private Integer def; // 防御力
    // 初始化状态
    public void init() {
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }
    // 战斗到0
    public void fight() {
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }
    // 保存角色状态
    public RoleStateMemento saveState() {
        return new RoleStateMemento(this.vit, this.atk, this.def);
    }
    // 回复角色状态
    public void recoverState(Memento memento) {
        RoleStateMemento roleStateMemento = (RoleStateMemento) memento;
        this.vit = roleStateMemento.getVit();
        this.atk = roleStateMemento.getAtk();
        this.def = roleStateMemento.getDef();
    }
    // 展示状态
    public void showState() {
        System.out.println("角色生命力:" + this.vit);
        System.out.println("角色攻击力:" + this.atk);
        System.out.println("角色防御力:" + this.def);
    }
    // 备忘录内部类
    @Data
    @AllArgsConstructor
    private class RoleStateMemento implements Memento {
        private Integer vit; // 生命力
        private Integer atk; // 攻击力
        private Integer def; // 防御力
    }
}
    // 测试结果
    public static void main(String[] args){
      System.out.println("===========打boss前状态===========");
      GameRole gameRole = new GameRole();
      gameRole.init();
      gameRole.showState();
      // 保存进度
      RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
      roleStateCaretaker.setMemento(gameRole.saveState());
      System.out.println("===========打boss后状态===========");
      gameRole.fight();
      gameRole.showState();
      System.out.println("===========恢复状态===========");
      gameRole.recoverState(roleStateCaretaker.getMemento());
      gameRole.showState();
      // ===========打boss前状态===========
      // 角色生命力:100
      // 角色攻击力:100
      // 角色防御力:100
      // ===========打boss后状态===========
      // 角色生命力:0
      // 角色攻击力:0
      // 角色防御力:0
      // ===========恢复状态===========
      // 角色生命力:100
      // 角色攻击力:100
      // 角色防御力:100
    }

1.3 总结

适用场景

  • 需要保存历史快照的场景。
    -希望在对象之外保存状态,且除了自己其他类对象无法访问状态保存具体内容。

优点:

-简化发起人实体类职责,隔离状态存储与获取,实现了信息的封装,客户端无需关心状态的保存细节。
-提供状态回滚功能。

缺点:

  • 消耗资源:如果需要保存的状态过多时,每一次保存都会消耗很多内存

你可能感兴趣的:(java设计模式,java,设计模式,备忘录模式)