Memento pattern provides a way to save the current state of an object and restore it later without exposing the implementation details of that object.
The Memento pattern is composed of three objects: the originator, the memento and the caretaker. The originator has the state that needs to be saved. The memento stores the saved state. The caretaker uses the originator and provides undo operation for it.
The internal state of the originator object is hidden from the outside world. An external object cannot save its state, therefore the originator has the responsibility to create a memento. Obviously, it can access its own private fields and can decide which fields have to be saved. The same applies when the state is restored from a memento. The memento is passed to the originator and originator knows how to use it.
The memento object is not allowed to change state. It should never store references to other objects those can be changed from outside.
Memento is a behavioral pattern.
The next Java example is about a very simple adder. It receives numbers one by one, stores them and calculates their sum after every step. It also supports undo operation, so when accidentally a wrong number is added in, it can be removed.
The Addition
class is the originator in this example.
Its add
method receives a number and add it to the numbers
list.
This list is the state of the object that needs to be saved.
The saveState
method creates a Memento
instance.
Instead of giving the numbers
list directly to it,
the list is converted to an array, and that array is given to the Memento
.
This array is a new object, and any other modification to the numbers
list has no effect to it.
If the reference to numbers
list were saved by the memento, that would be a design flaw.
In that case, the memento would change state when the originator do.
The restoreState
method is the inverse of the saveState
method.
It gets the array from the Memento
, converts it to a List
and
sets the list as actual state.
package com.programcodex.designpatterns.memento;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Addition {
private List<Integer> numbers = new ArrayList<>();
public void add(Integer number) {
this.numbers.add(number);
}
public Memento saveState() {
return new Memento(numbers.toArray(new Integer[0]));
}
public void restoreState(Memento memento) {
this.numbers = Arrays.stream(memento.getNumbers())
.collect(Collectors.toList());
}
@Override
public String toString() {
if (numbers.isEmpty()) {
return "\nThere's no any number.";
}
int result = 0;
StringBuilder sb = new StringBuilder("\n");
for (Integer n : numbers) {
sb.append(" + ").append(n);
result += n;
}
sb.append(" = ").append(result);
return sb.toString().replaceFirst(" \\+ ", "");
}
}
The Memento
class is very simple.
It only stores the state of the above object.
package com.programcodex.designpatterns.memento;
public class Memento {
private final Integer[] numbers;
public Memento(Integer[] numbers) {
this.numbers = numbers;
}
public Integer[] getNumbers() {
return numbers;
}
}
The Adder
class is the caretaker in this example.
It uses an instance of Addition
class.
Its add
method calls the add
method of the Addition
instance,
but before that, it gets a Memento
from the current state of the Addition
and stores it in the history
stack.
The undo
method gets the previous state from the history
stack
and gives it to the Addition
object to restore its state.
The addition
object is printed out after every modification from both methods.
Note: This example do not support redo operation because the pop
method removes
the state from the stack.
If redo were needed, maybe a List
would be a better choice instead of the Stack
.
package com.programcodex.designpatterns.memento;
import java.util.Stack;
public class Adder {
private Addition addition = new Addition();
private Stack<Memento> history = new Stack<>();
public void add(Integer number) {
history.push(addition.saveState());
addition.add(number);
System.out.println(addition);
}
public void undo() {
Memento lastState = history.pop();
addition.restoreState(lastState);
System.out.println(addition);
}
}
And we are done. It’s time to run these things.
An Adder
is instantiated and numbers are added to it.
After that, every step gets undone.
package com.programcodex.designpatterns.memento;
public class TestMemento {
public static void main(String[] args) {
Adder adder = new Adder();
System.out.println("Let's sum some numbers:");
adder.add(1);
adder.add(2);
adder.add(3);
adder.add(4);
System.out.println("\n\nIt's no good. "
+ "Let's undo everything:");
adder.undo();
adder.undo();
adder.undo();
adder.undo();
}
}
The output of the above test class: