备忘录模式--游戏存档

引子

小帅在一家游戏公司工作。最近领导让他设计游戏的自动存档功能,每次玩家要挑战boss的时候,系统要能够实现自动存档。如果玩家挑战失败,game over 了,还能恢复到挑战之前的状态再来一次。

小帅心想,我用一个备份对象把所有的游戏参数记录下来,当玩家要读取存档的时候,再把备份对象里的数据恢复回来不就行了吗?

普通方法

小帅很快就写出了代码:

/**
 * 英雄类
 */
public class Hero {

    /**
     * 生命值
     */
    private int healthPoint;

    /**
     * 魔法值
     */
    private int magicalValue;

    /**
     * 攻击力
     */
    private int attackPower;

    public Hero(int healthPoint, int magicalValue, int attackPower) {
        this.healthPoint = healthPoint;
        this.magicalValue = magicalValue;
        this.attackPower = attackPower;
    }

    /**
     * 游戏结束
     */
    public void gameOver() {
        this.healthPoint = 0;
        this.magicalValue = 0;
        this.attackPower = 0;
    }

    /**
     * 设置属性
     * @param healthPoint
     * @param magicalValue
     * @param attackPower
     */
    public void setState(int healthPoint, int magicalValue, int attackPower) {
        this.healthPoint = healthPoint;
        this.magicalValue = magicalValue;
        this.attackPower = attackPower;
    }


    @Override
    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append("生命值:" + this.healthPoint + "\n");
        display.append("魔法值:" + this.magicalValue + "\n");
        display.append("攻击力:" + this.attackPower + "\n");
        return display.toString();
    }

    public int getHealthPoint() {
        return healthPoint;
    }

    public void setHealthPoint(int healthPoint) {
        this.healthPoint = healthPoint;
    }

    public int getMagicalValue() {
        return magicalValue;
    }

    public void setMagicalValue(int magicalValue) {
        this.magicalValue = magicalValue;
    }

    public int getAttackPower() {
        return attackPower;
    }

    public void setAttackPower(int attackPower) {
        this.attackPower = attackPower;
    }
}
/**
 * 客户端类
 */
public class Client {

    public static void main(String[] args) {
        Hero hero = new Hero(90,85,70);
        // 挑战boss之前的状态
        System.out.println("挑战boss之前的状态:\n" + hero);
        // 保存进度
        Hero heroBackUp = new Hero(hero.getHealthPoint(), hero.getMagicalValue(), hero.getAttackPower());
        // 挑战失败
        hero.gameOver();
        System.out.println("挑战失败后的状态:\n" + hero);
        // 恢复进度
        hero.setState(heroBackUp.getHealthPoint(), heroBackUp.getMagicalValue(), heroBackUp.getAttackPower());
        System.out.println("恢复进度后的状态:\n" + hero);
    }
}

输出:

挑战boss之前的状态:
生命值:90
魔法值:85
攻击力:70

挑战失败后的状态:
生命值:0
魔法值:0
攻击力:0

恢复进度后的状态:
生命值:90
魔法值:85
攻击力:70

小帅觉得这不是很简单吗?

我只要再建个heroBackUp对象,把hero对象的状态保存进去,等需要读档的时候再读取heroBackUp对象中的状态不就行了吗?

这时候项目组里的老王发话了:

你直接用Hero类的对象作为备份,方便是方便,但是不安全,Hero类中有属性公有的set方法。备份的heroBackUp对象,有可能会被别人调用set方法更改属性。

备份对象是只读的,不能被修改。

在这里插入图片描述
heroBackUp对象可能会被其他对象修改属性,这违背了封装原则。

那如何才能在不违背封装原则的前提下,备份一个对象呢?小帅连忙问道。

老王微微一笑:这就要用到备忘录模式了。

备忘录模式

备忘录模式:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

在这里插入图片描述

  • Memento(备忘录):备忘录存储原发器对象的内部状态;备忘录的内部状态只能由原发器访问。
  • Originator(原发器):创建备忘录,记录当前的状态;使用备忘录恢复状态。
  • Caretaker(负责人):管理备忘录;不能对备忘录的内容进行操作或检查。

老王没多久就把代码改造好了:

Originator类:

/**
 * 英雄类(就是Originator)
 */
public class Hero {

    /**
     * 生命值
     */
    private int healthPoint;

    /**
     * 魔法值
     */
    private int magicalValue;

    /**
     * 攻击力
     */
    private int attackPower;

    public Hero(int healthPoint, int magicalValue, int attackPower) {
        this.healthPoint = healthPoint;
        this.magicalValue = magicalValue;
        this.attackPower = attackPower;
    }

    /**
     * 游戏结束
     */
    public void gameOver() {
        this.healthPoint = 0;
        this.magicalValue = 0;
        this.attackPower = 0;
    }


    /**
     * 创建备忘录
     * @return
     */
    public Memento createMemento() {
        return new Memento(healthPoint, magicalValue, attackPower);
    }

    /**
     * 从备忘录中恢复数据
     * @param memento
     */
    public void restoreMemento(Memento memento) {
        this.healthPoint = memento.getHealthPoint();
        this.magicalValue = memento.getMagicalValue();
        this.attackPower = memento.getAttackPower();
    }

    @Override
    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append("生命值:" + this.healthPoint + "\n");
        display.append("魔法值:" + this.magicalValue + "\n");
        display.append("攻击力:" + this.attackPower + "\n");
        return display.toString();
    }
}

Memento :

/**
 * 备忘录类
 */
public class Memento {

    /**
     * 生命值
     */
    private int healthPoint;

    /**
     * 魔法值
     */
    private int magicalValue;

    /**
     * 攻击力
     */
    private int attackPower;


    public Memento(int healthPoint, int magicalValue, int attackPower) {
        this.healthPoint = healthPoint;
        this.magicalValue = magicalValue;
        this.attackPower = attackPower;
    }

    /**
     * 备忘录中只有get方法,没有set方法,因为备忘录中的数据不应该被修改
     * @return
     */
    public int getHealthPoint() {
        return healthPoint;
    }

    /**
     * 备忘录中只有get方法,没有set方法,因为备忘录中的数据不应该被修改
     * @return
     */
    public int getMagicalValue() {
        return magicalValue;
    }

    /**
     * 备忘录中只有get方法,没有set方法,因为备忘录中的数据不应该被修改
     * @return
     */
    public int getAttackPower() {
        return attackPower;
    }

}

Caretaker:

/**
 * 负责人类
 */
public class Caretaker {

    /**
     * 备忘录
     */
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }

}

Client :

/**
 * 客户端类
 */
public class Client {
    public static void main(String[] args) {
        Hero hero = new Hero(90,85,70);
        // 挑战boss之前的状态
        System.out.println("挑战boss之前的状态:\n" + hero);
        // 保存进度
        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(hero.createMemento());
        // 挑战失败
        hero.gameOver();
        System.out.println("挑战失败后的状态:\n" + hero);
        // 恢复进度
        hero.restoreMemento(caretaker.getMemento());
        System.out.println("恢复进度后的状态:\n" + hero);
    }
}

输出:

挑战boss之前的状态:
生命值:90
魔法值:85
攻击力:70

挑战失败后的状态:
生命值:0
魔法值:0
攻击力:0

恢复进度后的状态:
生命值:90
魔法值:85
攻击力:70

我定义一个独立的类(Memento 类)来表示备份,而不是复用 Hero类。这个类只暴露 get() 方法,没有 set() 等任何修改内部状态的方法。这样就保证了数据不会被修改,符合了封装原则。

Caretaker类专门负责管理Memento类,但是Caretaker类对Memento类的权限有限,不能修改Memento类的数据。

老王总结道。

撤销功能的实现

老王接着说:如果我们想实现常见的撤销功能,可以在Caretaker类中用Stack来存储Memento对象。

每进行一次操作就调用Stack的push()方法把一个Memento对象入栈。

撤销的时候就调用Stack的pop()方法出栈,取出一个Memento对象,来恢复状态。

小帅听完不禁赞叹:你可比我家隔壁的老王厉害多了!

总结

当你需要创建对象状态快照来恢复其之前的状态时, 可以使用备忘录模式。

该模式建议将对象状态的副本存储在一个名为备忘录 (Memento) 的特殊对象中。 备忘录让对象自行负责创建其状态的快照,除了创建备忘录的对象外, 任何其他对象都不能访问备忘录的内容。

Caretaker对象必须使用受限接口与备忘录进行交互, 它可以获Memento对象本身, 但它不能获取和更改Memento对象中的属性状态。

优点

  • 可以在保持封装状态的情况下,创建对象状态的备忘录,保证了备忘录数据的安全。
  • 可以通过让负责人维护备忘录,来简化原发器代码。

缺点

  • 如果客户端过于频繁地创建备忘录, 程序将消耗大量内存。

代码链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下捕获对象的内部状态,并在对象之外保存这个状态,以便在以后的某个时候将其恢复到原先的状态。 备忘录模式由三个主要角色组成: 1. 发起人(Originator):负责创建一个备忘录,并记录和恢复自身的内部状态。 2. 备忘录(Memento):负责存储发起人对象的内部状态。 3. 管理者(Caretaker):负责保存备忘录,并可以对其进行管理,但不知道备忘录的具体内容。 该模式的工作流程如下: 1. 发起人创建一个备忘录,将自己的状态保存在备忘录中。 2. 发起人可以根据需要在任何时候保存自己的状态。 3. 管理者将备忘录保存起来,可维护多个备忘录。 4. 在需要恢复状态的时候,可以通过管理者获取相应的备忘录,并将发起人的状态恢复到备忘录中保存的状态。 备忘录模式的优点是: - 发起人对象与备忘录对象分离,实现了信息的封装和隐藏。 - 备忘录对象存储了发起人对象的历史状态,可以实现撤销和恢复操作。 - 备忘录可以进行存储和管理,可进行序列化和持久化操作。 备忘录模式通常适用于以下情况: - 需要保存和恢复对象的状态,并且封装对象的状态对于其他对象来说是不可见的。 - 需要实现撤销和恢复功能。 - 需要保存和恢复部分或全部对象状态的场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值