Memo Mode--Game Archive

Primer

Xiaoshuai works in a game company. Recently, the leader asked him to design the automatic save function of the game. Every time the player wants to challenge the boss, the system must be able to realize automatic save. If the player fails the challenge and the game is over, they can return to the state before the challenge and try again.

Xiaoshuai thought to himself, I use a backup object to record all the game parameters, and when the player wants to read the save file, wouldn’t it be all right to restore the data in the backup object?

common method

Xiaoshuai quickly wrote the code:

/**
 * 英雄类
 */
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);
    }
}

output:

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

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

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

Xiaoshuai thinks this is not very simple?

I just need to build another heroBackUp object, save the state of the hero object in it, and then read the state in the heroBackUp object when I need to read the file, isn’t it all right?

At this time, Lao Wang in the project team spoke:

You directly use the object of the Hero class as a backup, which is convenient, but not safe. The Hero class has a set method with public attributes. The backed up heroBackUp object may be called by others to change the properties of the set method.

Backup objects are read-only and cannot be modified.

insert image description here
The heroBackUp object may be modified by other objects, which violates the principle of encapsulation.

So how can we back up an object without violating the principle of encapsulation? Xiaoshuai asked quickly.

Lao Wang smiled slightly: This will use the memo mode.

memo mode

Memento mode: Capture the internal state of an object and save it outside the object without violating the principle of encapsulation. This allows the object to be restored to its previously saved state later.

The Memento pattern is a behavioral design pattern that allows saving and restoring the previous state of an object without exposing the details of the object's implementation.

insert image description here

  • Memento: The memento stores the internal state of the originator object; the internal state of the memento is only accessible by the originator.
  • Originator: Create a memo to record the current state; use the memo to restore the state.
  • Caretaker (person in charge): manages the memo; cannot manipulate or inspect the contents of the memo.

It didn't take long for Lao Wang to modify the code:

Originator class:

/**
 * 英雄类(就是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);
    }
}

output:

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

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

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

Instead of reusing the Hero class, I define a separate class (Memento class) to represent the backup. This class only exposes the get() method, without any method of modifying the internal state such as set(). This ensures that the data will not be modified and conforms to the encapsulation principle.

The Caretaker class is specifically responsible for managing the Memento class, but the Caretaker class has limited permissions to the Memento class and cannot modify the data of the Memento class.

Lao Wang concluded.

Implementation of undo function

Lao Wang went on to say: If we want to implement common undo functions, we can use Stack to store Memento objects in the Caretaker class.

Call the push() method of Stack to push a Memento object onto the stack every time an operation is performed.

When canceling, call Stack's pop() method to pop out of the stack, and take out a Memento object to restore the state.

After hearing this, Xiaoshuai couldn't help admiring: You are much better than the old Wang next door to me!

Summarize

The memento pattern can be used when you need to create a snapshot of object state to restore its previous state.

The pattern proposes to store a copy of an object's state in a special object called a Memento. Memento makes the object responsible for creating a snapshot of its state, and no other object can access the content of the memento except the object that created the memo.

The Caretaker object must use a restricted interface to interact with the Memento. It can get the Memento object itself, but it cannot get and change the state of the properties in the Memento object.

advantage

  • The memo of the object state can be created while maintaining the encapsulation state, which ensures the security of the memo data.
  • The originator code can be simplified by having the responsible person maintain the memento.

shortcoming

  • If the client creates memos too frequently, the program will consume a lot of memory.

code link

Guess you like

Origin blog.csdn.net/zhanyd/article/details/120056504