Token
Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
Real-world example
We are working on an astrology application where we need to analyze star properties over time. We are creating snapshots of star states using the Memento pattern.
In plain words
Memento pattern captures object internal state making it easy to store and restore objects in any point of time.
Wikipedia says
The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
Programmatic Example
Let’s first define the types of stars we are capable to handle.
1public enum StarType {
2 SUN("sun"),
3 RED_GIANT("red giant"),
4 WHITE_DWARF("white dwarf"),
5 SUPERNOVA("supernova"),
6 DEAD("dead star");
7 ...
8}
Next, let’s jump straight to the essentials. Here’s the Star
class along with the mementos that we
need to manipulate. Especially pay attention to getMemento
and setMemento
methods.
1public interface StarMemento {
2}
3
4public class Star {
5
6 private StarType type;
7 private int ageYears;
8 private int massTons;
9
10 public Star(StarType startType, int startAge, int startMass) {
11 this.type = startType;
12 this.ageYears = startAge;
13 this.massTons = startMass;
14 }
15
16 public void timePasses() {
17 ageYears *= 2;
18 massTons *= 8;
19 switch (type) {
20 case RED_GIANT -> type = StarType.WHITE_DWARF;
21 case SUN -> type = StarType.RED_GIANT;
22 case SUPERNOVA -> type = StarType.DEAD;
23 case WHITE_DWARF -> type = StarType.SUPERNOVA;
24 case DEAD -> {
25 ageYears *= 2;
26 massTons = 0;
27 }
28 default -> {
29 }
30 }
31 }
32
33 StarMemento getMemento() {
34 var state = new StarMementoInternal();
35 state.setAgeYears(ageYears);
36 state.setMassTons(massTons);
37 state.setType(type);
38 return state;
39 }
40
41 void setMemento(StarMemento memento) {
42 var state = (StarMementoInternal) memento;
43 this.type = state.getType();
44 this.ageYears = state.getAgeYears();
45 this.massTons = state.getMassTons();
46 }
47
48 @Override
49 public String toString() {
50 return String.format("%s age: %d years mass: %d tons", type.toString(), ageYears, massTons);
51 }
52
53 private static class StarMementoInternal implements StarMemento {
54
55 private StarType type;
56 private int ageYears;
57 private int massTons;
58
59 // setters and getters ->
60 ...
61 }
62}
And finally here’s how we use the mementos to store and restore star states.
1 var states = new Stack<>();
2 var star = new Star(StarType.SUN, 10000000, 500000);
3 LOGGER.info(star.toString());
4 states.add(star.getMemento());
5 star.timePasses();
6 LOGGER.info(star.toString());
7 states.add(star.getMemento());
8 star.timePasses();
9 LOGGER.info(star.toString());
10 states.add(star.getMemento());
11 star.timePasses();
12 LOGGER.info(star.toString());
13 states.add(star.getMemento());
14 star.timePasses();
15 LOGGER.info(star.toString());
16 while (states.size() > 0) {
17 star.setMemento(states.pop());
18 LOGGER.info(star.toString());
19 }
Program output:
sun age: 10000000 years mass: 500000 tons
red giant age: 20000000 years mass: 4000000 tons
white dwarf age: 40000000 years mass: 32000000 tons
supernova age: 80000000 years mass: 256000000 tons
dead star age: 160000000 years mass: 2048000000 tons
supernova age: 80000000 years mass: 256000000 tons
white dwarf age: 40000000 years mass: 32000000 tons
red giant age: 20000000 years mass: 4000000 tons
sun age: 10000000 years mass: 500000 tons
Use the Memento pattern when