大话设计模式笔记(十五)の备忘录模式

举个栗子

问题描述

打游戏存进度。

简单实现

GameRole

/**
 * 游戏角色
 * Created by callmeDevil on 2019/8/11.
 */
public class GameRole {

    // 生命力
    private int vit;
    // 攻击力
    private int atk;
    // 防御力
    private int def;

    // 状态显示
    public void stateDisplay() {
        System.out.println("角色当前状态:");
        System.out.println(String.format("体力:%s", this.vit));
        System.out.println(String.format("攻击力:%s", this.atk));
        System.out.println(String.format("防御力:%s", this.def));
        System.out.println();
    }

    //  获得初始状态
    public void getInitState() {
        // 数据通常来自本机磁盘或远程数据库
        this.vit = 100;
        this.atk = 100;
        this.def = 100;
    }

    // 战斗
    public void fight(){
        // 在与Boss大战后游戏数据损耗为0
        this.vit = 0;
        this.atk = 0;
        this.def = 0;
    }

    // 省略 get set
    
}

测试

public class Test {
    public static void main(String[] args) {
        // 大战Boss前
        GameRole lufi = new GameRole();
        // 获得初始角色状态
        lufi.getInitState();
        lufi.stateDisplay();
        // 通过“游戏角色”新实例,保存进度
        GameRole backup = new GameRole();
        backup.setVit(lufi.getVit());
        backup.setAtk(lufi.getAtk());
        backup.setDef(lufi.getDef());
        // 大战Boss时,损耗严重,全部为0
        lufi.fight();
        lufi.stateDisplay();
        // GameOver不甘心,恢复进度,重新玩过
        lufi.setVit(backup.getVit());
        lufi.setAtk(backup.getAtk());
        lufi.setDef(backup.getDef());
        lufi.stateDisplay();
    }
}

测试结果

角色当前状态:
体力:100
攻击力:100
防御力:100

角色当前状态:
体力:0
攻击力:0
防御力:0

角色当前状态:
体力:100
攻击力:100
防御力:100

存在问题

在客户端调用这段,把整个游戏角色的细节暴露了,职责太大,需要知道游戏角色的生命力、攻击力、防御力这些细节,还要进行备份。如果以后需要增加“魔法力”或修改现有的某种力,那这部分代码就需要修改,同样恢复时也是一样的问题。

备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

UML图

大话设计模式笔记(十五)の备忘录模式

代码实现

GameRole

/**
 * 游戏角色
 * Created by callmeDevil on 2019/8/11.
 */
public class GameRole {

    // 属性与简单实现GameRole相同

    // 保存角色状态
    public RoleStateMemento saveState() {
        return new RoleStateMemento(vit, atk, def);
    }

    // 恢复角色状态
    public void recoveryState(RoleStateMemento memento) {
        this.vit = memento.getVit();
        this.atk = memento.getAtk();
        this.def = memento.getDef();
    }

    // 其余方法与简单实现相同
    
}

RoleStateMemento

/**
 * 角色状态存储类
 * Created by callmeDevil on 2019/8/11.
 */
public class RoleStateMemento {

    // 属性与 简单实现 GameRole 相同

    // 将生命力、攻击力、防御力存入状态存储箱对象中
    public RoleStateMemento(int vit, int atk, int def){
        this.vit = vit;
        this.atk = atk;
        this.def = def;
    }

    // 省略 get set

}

RoleStateCaretaker

/**
 * 游戏状态管理者
 * Created by callmeDevil on 2019/8/11.
 */
public class RoleStateCaretaker {

    private RoleStateMemento memento;

    // 省略 get set

}

测试

public class Test {
    public static void main(String[] args) {
        // 大战Boss前
        GameRole lufi = new GameRole();
        lufi.getInitState();
        lufi.stateDisplay();
        // 保存游戏进度
        RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
        stateAdmin.setMemento(lufi.saveState());// 将具体数据封装在了 Memento中
        // 大战Boss时,损耗严重
        lufi.fight();
        lufi.stateDisplay();
        // 恢复状态
        lufi.recoveryState(stateAdmin.getMemento());
        lufi.stateDisplay();
    }
}

测试结果

与简单实现相同

总结

  • 把要保存的细节给封装在了 Memento 中,哪一天要更改保存细节也不用影响客户端。
  • 备忘录模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性的一小部分时,Originator 可以根据保存的 Memento 信息还原到前一状态。
  • 如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
  • 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来。
上一篇:yzoj 2345 战争 题解


下一篇:六、行为型模式--->10.备忘录模式