Introduction
Xiaoshuai works in a game company. Recently, the leader asked him to design the automatic archiving function of the game. Every time the player wants to challenge the boss, the system should be able to realize automatic archiving. If the player fails the challenge and the game is over, he can return to the state before the challenge and try again.
Xiaoshuai thought, I use a backup object to record all game parameters. When players want to read the archive, won't it be OK to restore the data in the backup object?
Common method
Xiaoshuai quickly wrote the code:
/** * Hero class */ public class Hero { /** * Life value */ private int healthPoint; /** * Magic value */ private int magicalValue; /** * aggressivity */ private int attackPower; public Hero(int healthPoint, int magicalValue, int attackPower) { this.healthPoint = healthPoint; this.magicalValue = magicalValue; this.attackPower = attackPower; } /** * game over */ public void gameOver() { this.healthPoint = 0; this.magicalValue = 0; this.attackPower = 0; } /** * set a property * @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("HP:" + this.healthPoint + "\n"); display.append("Magic value:" + this.magicalValue + "\n"); display.append("aggressivity:" + 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; } }
/** * Client class */ public class Client { public static void main(String[] args) { Hero hero = new Hero(90,85,70); // Status before challenging boss System.out.println("Challenge boss Previous status:\n" + hero); // Save progress Hero heroBackUp = new Hero(hero.getHealthPoint(), hero.getMagicalValue(), hero.getAttackPower()); // Challenge failure hero.gameOver(); System.out.println("Status after challenge failure:\n" + hero); // Recovery progress hero.setState(heroBackUp.getHealthPoint(), heroBackUp.getMagicalValue(), heroBackUp.getAttackPower()); System.out.println("Status after recovery progress:\n" + hero); } }
Output:
Challenge boss Previous status: HP: 90 Magic value: 85 Attack power: 70 Status after challenge failure: HP: 0 Magic value: 0 Attack power: 0 Status after recovery progress: HP: 90 Magic value: 85 Attack power: 70
Doesn't Xiaoshuai think it's very simple?
I just need to create another heroBackUp object, save the state of the hero object, and then read the state of the heroBackUp object when I need to read the file?
At this time, Lao Wang in the project team spoke:
You can 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 property of the backed up heroBackUp object may be changed by someone else calling the set method.
The backup object is read-only and cannot be modified.
The properties of the heroBackUp object may be modified by other objects, which violates the encapsulation principle.
How can you back up an object without violating the encapsulation principle? Xiaoshuai asked quickly.
Lao Wang smiled: this will use the memo mode.
Memo mode
Memo mode: capture the internal state of an object and save the state outside the object without violating the encapsulation principle. This will restore the object to its original saved state later.
Memo pattern is a behavior design pattern that allows you to save and restore the previous state of an object without exposing the implementation details of the object.
- Memo: the memo stores the internal state of the initiator object; The internal state of the memo can only be accessed by the originator.
- Originator: create a memo to record the current status; Restore state using memo.
- Caretaker (person in charge): management memorandum; The contents of the memo cannot be operated or checked.
Lao Wang transformed the code before long:
Originator class:
/** * Hero (i.e. Originator) */ public class Hero { /** * Life value */ private int healthPoint; /** * Magic value */ private int magicalValue; /** * aggressivity */ private int attackPower; public Hero(int healthPoint, int magicalValue, int attackPower) { this.healthPoint = healthPoint; this.magicalValue = magicalValue; this.attackPower = attackPower; } /** * game over */ public void gameOver() { this.healthPoint = 0; this.magicalValue = 0; this.attackPower = 0; } /** * Create memo * @return */ public Memento createMemento() { return new Memento(healthPoint, magicalValue, attackPower); } /** * Recover data from memo * @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("HP:" + this.healthPoint + "\n"); display.append("Magic value:" + this.magicalValue + "\n"); display.append("aggressivity:" + this.attackPower + "\n"); return display.toString(); } }
Memento :
/** * Memo class */ public class Memento { /** * Life value */ private int healthPoint; /** * Magic value */ private int magicalValue; /** * aggressivity */ private int attackPower; public Memento(int healthPoint, int magicalValue, int attackPower) { this.healthPoint = healthPoint; this.magicalValue = magicalValue; this.attackPower = attackPower; } /** * There is only get method but no set method in the memo, because the data in the memo should not be modified * @return */ public int getHealthPoint() { return healthPoint; } /** * There is only get method but no set method in the memo, because the data in the memo should not be modified * @return */ public int getMagicalValue() { return magicalValue; } /** * There is only get method but no set method in the memo, because the data in the memo should not be modified * @return */ public int getAttackPower() { return attackPower; } }
Caretaker:
/** * Responsible human */ public class Caretaker { /** * memorandum */ private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
Client :
/** * Client class */ public class Client { public static void main(String[] args) { Hero hero = new Hero(90,85,70); // Status before challenging boss System.out.println("Challenge boss Previous status:\n" + hero); // Save progress Caretaker caretaker = new Caretaker(); caretaker.setMemento(hero.createMemento()); // Challenge failure hero.gameOver(); System.out.println("Status after challenge failure:\n" + hero); // Recovery progress hero.restoreMemento(caretaker.getMemento()); System.out.println("Status after recovery progress:\n" + hero); } }
Output:
Challenge boss Previous status: HP: 90 Magic value: 85 Attack power: 70 Status after challenge failure: HP: 0 Magic value: 0 Attack power: 0 Status after recovery progress: HP: 90 Magic value: 85 Attack power: 70
I define a separate class (Memento class) to represent backup instead of reusing Hero class. This class only exposes the get() method and does not have any methods such as set() to modify the internal state. This ensures that the data will not be modified and conforms to the encapsulation principle.
Caretaker class is specially responsible for managing Memento class, but caretaker class has limited permissions on Memento class and cannot modify Memento class data.
Lao Wang concluded.
Implementation of revocation function
Lao Wang continued: if we want to implement common Undo functions, we can use Stack to store Memento objects in the Caretaker class.
Every time an operation is performed, the push() method of Stack is called to put a Memento object on the Stack.
When canceling, call the pop() method of Stack to get out of the Stack and take out a Memento object to restore the state.
Xiaoshuai couldn't help exclaiming: you are much better than Lao Wang next door!
summary
When you need to create a snapshot of the state of an object to restore its previous state, you can use memo mode.
This pattern recommends storing a copy of the object state in a special object called a memo. The memo allows the object to create a snapshot of its state. Except for the object that created the memo, no other object can access the contents of the memo.
The Caretaker object must use a restricted interface to interact with the memo. It can get the Memento object itself, but it cannot get and change the property state in the Memento object.
advantage
- The memo of object state can be created while maintaining the encapsulated state, so as to ensure the security of memo data.
- You can simplify the originator code by having the owner maintain the memo.
shortcoming
- If the client creates memos too often, the program will consume a lot of memory.