“破镜重圆”,聊聊备忘录模式

193 阅读2分钟

设计模式-备忘录模式

"谁说破镜不能重圆?"

现实例子

对于大多数的单机类游戏来说,
由于故事线太长,游戏内容太丰富,可能因为游玩时间不够或者想要得到更好的游戏体验 ,
游戏玩家很难一次就满意地通关

因此就产生了经典的存档读档模式,
玩家只需在某个时刻进行存档,那么后续哪怕死亡,只需要读挡就会瞬间复活

这里的存档就是备忘录

代码结构

memo.png

原发器Originator

需要保存状态的类,如游戏角色

  • 各种内部属性(包含了需要保存的状态属性)
  • 保存状态方法
  • 读取状态方法

备忘录Memento

真正保存状态的类,如保存角色的属性和状态

为方便访问,可设计为原发器的内部类

  • 各种有关状态的属性

负责人Caretaker

管理备忘录的类
主要是为了避免备忘录直接暴露,因此多了一个管理类

  • 备忘录
  • 存储读取备忘录的方法

具体代码

根据单机游戏的角色闯关设定
与代码结构稍有不同,把备忘录从一个类变成备忘录的接口和具体备忘录
这么做的意义是可以减少属性的暴露,让负责人无法访问具体的属性

记录接口(备忘录接口)

/**
 * 备忘录 -> 档案接口
 * 窄接口,主要用于关闭负责人的访问属性权限
 */
public interface Record {
}

档案保存者(管理者)

/**
 * 管理者 -> 档案保存者
 */
public class RecordSaver {
    private Record record;
    public Record getRecord() {
        return record;
    }
    public void setRecord(Record record) {
        this.record = record;
    }
}

游戏角色(原发器)

/**
 *  Originator -> Person
 *  需要保存数据的类
 */
public class Person {
    String name;
    Integer level;
    Integer blood;

    public Person(String name) {
        this.name = name;
        this.level = 1;
        this.blood = 100;
    }

    public void die(){
        blood = 0;
        System.out.print("闯关中死亡 ");
        showState();
    }

    public void levelUp(){
        level++;
        System.out.print("闯关中升级 ");
        showState();
    }
    
    public void showState(){
        System.out.println(name +"当前 level:"+level+" , blood:"+blood);
    }

    public Record save(){
        System.out.println("角色存档");
        return new PersonRecord(level, blood);
    }

    public void load(Record record){
        System.out.println("角色读档");
        PersonRecord personRecord = (PersonRecord)record;
        blood = personRecord.blood;
        level = personRecord.level;
    }
}

记录(备忘录)内部类实现

/**
 *  Originator -> Person
 *  需要保存数据的类
 */
public class Person {
    // Person的属性与方法 上面已有,省略。

    public void load(Record record){
        System.out.println("角色读档");
        PersonRecord personRecord = (PersonRecord)record;
        blood = personRecord.blood;
        level = personRecord.level;
    }
}

调用与结果

Person ming = new Person("ming");
ming.levelUp();

RecordSaver recordSaver = new RecordSaver();
recordSaver.setRecord(ming.save());

ming.levelUp();
ming.die();

ming.load(recordSaver.getRecord());
ming.showState();

image.png

为什么不使用原型模式?

原型模式是克隆一个完整的对象,而恢复状态往往只需要部分的属性
完全的复制可能反而会导致额外的开销,甚至恢复了一些不该恢复的属性。

适用场景

  1. 需要恢复类的状态到之前的某一时刻
  2. 部分属性需要被记录

思维导图

image.png