Game character status recovery problem
the game characters have attack power and defense power. They save their state attack power and defense power before fighting the Boss. When fighting the Boss, their attack power and defense power decrease and recover from the memo object to the state before the war
Traditional scheme
object <-> Object state object <-> Object state object <-> Object state
Problem analysis of traditional methods
1. An object corresponds to an object that saves the state of the object. In this way, when there are many objects in our game, it is not conducive to management and costs a lot
2. The traditional way is to simply make a backup (New out another object), and then put the data to be backed up into the new object, but this exposes the details inside the object
Memo mode
memo pattern captures the internal state of an object without destroying encapsulation, and saves the state outside the object. This will restore the object to its original saved state later
the memo mode can be understood here: in real life, the memo is used to record some things to be done, or to record things that have reached a common opinion, so as not to forget. At the software level, the memo mode has the same meaning. The memo object is mainly used to record a certain state or some data of an object. When fallback is required, the original data can be obtained from the memo object for recovery
Memo mode structure
1. The Originator class can generate a snapshot of its own state, or restore its state through the snapshot when necessary.
2. A memo is a value object of the initiator state snapshot. The usual approach is to make the memo immutable and pass the data at one time through the constructor.
3. The Caretaker only knows "when" and "why" to capture the state of the initiator and when to restore the state.
the person in charge records the historical status of the initiator by saving the memo stack. When the originator needs to trace back the historical state, the person in charge will get the top memo from the stack and pass it to the recovery method of the originator.
In this implementation, the memo class will be nested in the originator. In this way, the initiator can access the member variables and methods of the memo, even if these methods are declared private. On the other hand, the owner has limited access to the member variables and methods of the memo: they can only save the memo in the stack, not modify its state.
Memo mode is suitable for application scenarios
1. When you need to create a snapshot of the state of an object to restore its previous state, you can use memo mode.
2. This pattern can be used when direct access to an object's member variable, getter, or setter will cause the encapsulation to break through.
Implementation mode
-
Determines the class that plays the role of the originator. It is important to determine whether the program uses one initiator center object or multiple smaller objects.
-
Create a memo class. Declare the memo member variables corresponding to each initiator member variable one by one.
-
Make the memo class immutable. Memos can only receive data once through constructors. This class cannot contain setters.
-
If your programming language supports nested classes, you can nest memos in the originator; If not, you can extract an empty interface from the memo class, and then let all other objects reference the memo through the interface. You can add some metadata operations to this interface, but you cannot expose the state of the initiator.
-
Add a method to create a memo in the originator. The originator must pass its state to the memo through one or more actual parameters of the memo constructor.
The type of the result returned by this method must be the interface you extracted in the previous step (if you have already extracted). In fact, the method that creates the memo must interact directly with the memo class.
-
Add a method to the initiator class to restore its own state. The method accepts a memo object as a parameter. If you extracted the interface in the previous steps, you can use the interface as the type of parameter. In this case, you need to cast the input object into a memo, because the initiator needs to have full access to the object.
-
Whether the owner is a command object, history, or something completely different, it must know when to request a new memo from the initiator, how to store the memo, and when to use a specific memo to recover the initiator.
-
The connection between the person in charge and the originator can be moved to the memo class. In this case, each memo must be connected to the originator that created it. The recovery method can also be moved to the memo class, but this method can only be implemented when the memo class is nested in the originator, or the originator class provides enough setters and can override its state.
Advantages and disadvantages of memo mode
advantages:
✔️ You can create object state snapshots without destroying object encapsulation.
✔️ You can simplify the originator code by having the owner maintain the originator status history.
disadvantages:
❌ If the client creates memos too often, the program will consume a lot of memory.
❌ The owner must fully track the life cycle of the initiator in order to destroy the discarded memo.
❌ Most dynamic programming languages (such as PHP, Python, and JavaScript) cannot ensure that the state in the memo is not modified.
Solve the problem of game character state recovery
code:
public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } }
public class GameRole { private String state; // status information public String getState(){return state;} public void setState(String state){this.state=state;} //Save status object Memento public Memento saveState(){ return new Memento(state); } //Restore state public void getStateFromMemento(Memento memento){ state = memento.getState(); } }
public class Caretaker { //Storage memo object private List<Memento> mementoList = new ArrayList<>(); public void add(Memento memento){ mementoList.add(memento); } //Gets the memo object for the desired subscript public Memento get(int index){ return mementoList.get(index); } }
public class Client { public static void main(String[] args) { GameRole gameRole = new GameRole(); Caretaker caretaker = new Caretaker(); gameRole.setState("State 1: attack power 100, defense power 50"); //Save status 1 caretaker.add(gameRole.saveState()); gameRole.setState("State 2: attack power 120, defense power 60"); //Save status 2 caretaker.add(gameRole.saveState()); gameRole.setState("State 3: attack power 150, defense power 70"); //Save status 3 caretaker.add(gameRole.saveState()); System.out.println("The current status is"+gameRole.getState()); System.out.println("Restore state 1"); gameRole.getStateFromMemento(caretaker.get(0)); System.out.println("-->"+gameRole.getState()); System.out.println("Restore state 3"); gameRole.getStateFromMemento(caretaker.get(2)); System.out.println("-->"+gameRole.getState()); } }
notes and details of memo mode
1. It provides users with a mechanism to restore the state, which can make it easier for users to return to a historical state
2. The encapsulation of information is realized, so that users do not need to care about the details of state preservation
3. If there are too many member variables of a class, it is bound to occupy a large amount of resources, and each save will consume a certain amount of memory [note]
4. Applicable application scenarios: 1) regret medicine. 2) Archive when playing games. 3) ctrl+ z in windows. 4) backward in ie. 5) Transaction management of database
5. To save memory, memo mode can be used in conjunction with prototype mode