设计模式系列19---可复原的备忘模式

enter image description here

玩游戏我们都知道有个东西叫自动存档,在我们遇到大Boss要打的时候,更是如此,一定要存档!
如果没有存档,死了就要你重新开始,如果是些大型游戏,已经花费了你很多时间,遇到大Boss,然后你被打死了,又没有存档,估计你就想直接把这个游戏卸载了。

有时偷懒,还去下载别人的通关档案回来覆盖本地的。
哈,我就曾经做过!还有一些已经刷满无限金币等的存档!
曾经还遇到过必须被大Boss打死才能继续的剧情,但因为外挂太牛逼,活生生把大Boss给打死了!搞到后面都没法继续,只能自己手动滚回某个档案,装怂,去被boss虐下。

所以很好理解,这个自动存档会保存我们当前的角色的状态,然后如果被大Boss打死了,就自动恢复到这个存档点状态,我们还可以继续去打大Boss,直到我们过关为止。

那么问题来了,我们该怎么用代码实现呢?

很显然就是用备忘录模式来做,也有翻译为纪念品的。

代码实现

现在我们先来准备下我们的主角

public class User {

    private int blood;//血量
    private int level;//等级
    private int force;//攻击力

    private String name;
    private int sex;

    public User(int blood, int level, int force, String name, int sex) {
        this.blood = blood;
        this.level = level;
        this.force = force;
        this.name = name;
        this.sex = sex;
    }

    public Memento saveState() {
        return new Memento(this.blood, this.level, this.force);
    }

    public void restoreState(int blood, int level, int force) {
        this.blood = blood;
        this.level = level;
        this.force = force;
    }


    public void setBlood(int blood) {
        this.blood = blood;
    }
}

我们的角色一般都有血量,等级和攻击力,还有名字和性别的属性。
现在我们设置只保存血量,等级和攻击力,对于名字和性别这个就不做保存,毕竟不可能打了下BOSS,就连名字和性别都换了…
所以我们来看下我们每次存档时候保存的信息内容的Memento

public class Memento {    
    private long time;
    private int blood;
    private int level;
    private int force;

    public Memento(int blood, int level, int force) {
        this.time = System.currentTimeMillis();
        this.blood = blood;
        this.level = level;
        this.force = force;
    }

    public long getTime() {
        return time;
    }

    public int getBlood() {
        return blood;
    }

    public int getLevel() {
        return level;
    }

    public int getForce() {
        return force;
    }
}

保存了血量,等级和攻击力,还有时间,当然需要时间啦。

接着就是一个来管理这对档案的一个类Caretaker

public class Caretaker {

    private List<Memento> mementoList = new ArrayList<>();

    public Memento getLastMemento() {
        return mementoList.get(mementoList.size() - 1);
    }

    public void addMemento(Memento memento) {
        mementoList.add(memento);
    }
}

他的作用是保存我们的档案记录,包括持久化到本地等操作。
但我们这里简单化了,直接是就用一个List来保存就好了,毕竟只是demo。
当然你也可以只用一个Memento来保存,毕竟也有些游戏限制只能有一个档案,新的会覆盖旧的档案。

有了这些准备,我们就可以来模拟下存档这个过程了

public class Client {

    public static void main(String[] args) {

        User user = new User(100, 99, 999, "jack", 0);
        Caretaker caretaker = new Caretaker();

        //在打boss前我们存档
        caretaker.addMemento(user.saveState());

        //被boss打死了,血量为0
        user.setBlood(0);

        //好在我们存档了,我们要恢复到最后保存的状态
        Memento memento = caretaker.getLastMemento();

        //好,我们又原地满血复活,可以继续去打Boss啦!
        user.restoreState(memento.getBlood(), memento.getLevel(), memento.getForce());      
    }
}

类图

enter image description here

这里的caretaker负责人就是来保存备忘录的,不会对备忘录的内容进行操作!
而那个Memento就是负责保存具体的内容
然后那个Originator原发器,是用于创建一个备忘录的,同时备忘录恢复内部状态的,对应于User。

这个可以用在我们的状态模型中去,如果某一部操作错误了,可以回滚回去。

后记

使用这个备忘录可能代价很高,因为他必须拷贝并存储大量的信息,或者用户频繁的存档和恢复的时候,也是不小问题,当然我们如果可以的话,可以考虑下增量存储的。

另外对于只支持一个档案的,可以考录下面这种简单的方式。

public class UserCloneable implements Cloneable {

    private UserCloneable backup;
    private int blood;//血量
    private int level;//等级
    private int force;//攻击力

    private String name;
    private int sex;

    public UserCloneable(int blood, int level, int force, String name, int sex) {
        this.blood = blood;
        this.level = level;
        this.force = force;
        this.name = name;
        this.sex = sex;
    }

    public void createState() throws CloneNotSupportedException {
        this.backup = (UserCloneable) this.clone();
    }

    public void restoreState() {
        setState(this.backup.blood, this.backup.level, this.backup.force);
    }

    private void setState(int blood, int level, int force) {
        this.blood = blood;
        this.level = level;
        this.force = force;
    }

    public void setBlood(int blood) {
        this.blood = blood;
    }
}

利用原型模型,简单的在类里面加多一个镜像!绝对的懒人方案。
不过请注意在写原型模型时候提到的浅拷贝深拷贝的问题。

热评文章