-
Notifications
You must be signed in to change notification settings - Fork 3
Momento
The Momento design pattern is useful in the following situation:
- Snapshots of the state of an object are required. This may be to support restoration of an object or undoable operations.
- The internals of the object should be kept hidden from external classes.
The object being tracked is called the originator.
The snapshots are referred to as momentos.
public class Originator {
private String mStatus = "initialized";
public Memento setStatus(String status) {
mStatus = status;
System.out.println("Status changed to: " + status);
}
public Memento createSnapshot() {
return new Memento(mStatus);
}
public void restoreSnapshot(Memento memento) {
mStatus = memento.mStatus;
System.out.println("Originator restored to status: " + mStatus);
}
// Used by an external history manager to restore Originator state.
// It is important to note that Momento data/methods should be private,
// making them only accessible by Originator instances.
public static class Memento {
private String mStatus;
public Memento(String status) {
mStatus = status;
}
}
}
Originator
instances are the only objects that an access or manipulate the data contained in Originator.Memento
objects.
By keeping Memento
objects opaque to external classes, this preserves the level of abstraction of the Originator
class. This keeps any unnecessary implementation details in the Originator
hidden from clients.
The Memento
class stores any data necessary to restore an Originator
to a previous state. This data can be incremental when a history of changes is preserved such that any state can be restored by reversing any subsequent changes.
Otherwise, it can store the complete state of the Originator
object at the time of the snapshot.
// Client usage
Originator originator = new Originator();
// History list
Stack<Originator.Memento> historyList = new Stack<>();
// Change status and keep history.
historyList.push(originator.createSnapshot());
originator.setStatus("pending");
historyList.push(originator.createSnapshot());
originator.setStatus("complete");
// Reverse status changes.
while(!stack.empty()) {
Origininator.Memento memento = stack.pop();
originator.restoreSnapshot(memento);
}
// Output:
// Status changed to: pending
// Status changed to: complete
// Originator restored to status: pending
// Originator restored to status: initialized
The Stack<Originator.Memento>
in the example above is known as a caretaker, which is responsible for managing Memento
instances.
The caretaker, however, should not have any knowledge of the contents or implementation details of any of the Memento
instances it oversees.
In order to process a Memento
object, it is passed back to the Originator
instance that spawned it.
The Memento
pattern can be used in conjuction with the Command design pattern.
public class ConcreteCommand implements Command {
private Originator.Momento mMomento;
private Originator mReceiver;
public ConcreteCommand(Originator receiver) {
mReceiver = receiver;
}
@Override
public void execute() {
// Create a snapshot before changes occur.
mMomento = mReceiver.createSnapshot();
mReceiver.execute();
}
@Override
public void undo() {
mReceiver.restoreSnapshot(mMomento);
}
}
In the example above, rolling back a change is supported, but the Momento
keeps the internals of the receiver hidden from the ConcreteCommand
class.