July 2, 2010 Design Patterns
July 2, 2010 Design Patterns
If you ever played any shooter game, you probably know what do hot-keys F5 & F9 mean. If you did not play those anyway you should be aware of usefulness of quick-save/quick-load functionality. By pressing F5 you save your current location your health/armor levels and maybe some other information, like how many monsters did you kill already. This defines your state which is going to be saved. On pressing F9 you return to your previous saved state (kind of undo operation).
When you are saving your state you don’t wanna it to be accessible by other classes (encapsulation of state), so you will be sure that none will decrease you health level. Also you wanna keep track of savings and maybe you are going to add functionality that will get back through two-three saves by pressing Shift+F9 (+F9).
How can you accomplish this?
Memento pattern is used when you want do undo operations without exposing internal state of the Originator (Game). Coordination of operations is done by Caretaker, or something that simply lists mementos and therefore remembers states without knowing what are they.
Let’s take a look at implementation:
Originator (Game)
public
GameMemento gameSave(){
return
new
GameMemento(_state);
}
public void loadGame(GameMemento memento){
_state = memento.getState();
}
public class
GameMemento {
private
final
GameState _state;
private GameMemento(GameState state) {
_state = state;
}
private GameState getState(){
return
_state;
}
}
}
So it is able to generate memento instance with current game state, as well it is able to get state from existing memento, BUT none else could get that state, since GameMemento is inner class of Game and methods are private.
Caretaker
Or the guy, who keeps track of your F5/F9th. Currently it is able to load the latest one quick save, but it could be easily enhanced.
public
Caretaker() {
_game = new
GameOriginator();
}
public void
shootThatDumbAss(){
_game.Play();
}
public void F5(){
_quickSaves.push(_game.gameSave());
}
public void F9(){
_game.loadGame(_quickSaves.peek());
}
}
Output
With following usage code:
our application generates:
Health: 100
Killed Monsters: 0
Health: 90
Killed Monsters: 2
Health: 81
Killed Monsters: 4
Health: 72
Killed Monsters: 6
Health: 64
Killed Monsters: 8
Health: 90
Killed Monsters: 2
Here is quick UML, that I drew for my example:
Markdown | Result |
---|---|
*text* | text |
**text** | text |
***text*** | text |
`code` | code |
~~~ more code ~~~~ |
more code |
[Link](https://www.example.com) | Link |
* Listitem |
|
> Quote | Quote |
Nice example!
Simple and good demonstration of memento goal.
I have one more idea for it's application.
Assume that we are developers of some game with rich gameplay interface, and there are PVP battles as one of it's aspects. Also there are mobs which also are able to participate in such battles (PVM in that case :) ).
It is obviously that our mob must have some AI approach. One of such approaches is bruteforce modeling moves, and selection from one of the best results for three next moves for example.
In this situation memento is very usefull. We can just save current state and perform move, evaluate next state, and then rollback to original.
You may ask me why I hilighted memento in such construction which is obvious for recursive approach. I think that in described case there is some responsibility dividing. Responsibility of memento is saving state. And Evalator just evaluates states without thinking about way, what they was obtained. Then there is not difference between modeling move and real move from the gameplay side, there is only some little lair and it "knows" that we are currently modeling, and then we have to make rollback.
Yea, good job on disquisition of responsibilities and exposing abstractions. One for saving/evaluation logic. Another for not caring about if this is usual Player (in PlayerVsPlayer) or if this is Mob (in PlayerVsMob).
An idea looks very good and robust. Have you ever tried it in your own applications?