Applied Design Patterns with Java
Behavioral :: Memento (283) {C ch 21}
Intent
Without violating encapsulation, capture and externalize an object's
internal state so that the object can be restored to this state later.
As Known As
Token
Motivation
Sometimes it's necessary to record the internal
state of an object. This is required when implementing checkpoints and undo mechanisms that let users back out
of tentative operations or recover from errors. State information must be saved so that objects can be restored
to their previous states. But objects normally encapsulate some or all of their state, making it inaccessible to
other objects and impossible to save externally. Exposing this state would violate encapsulation, which can compromise
the application's reliability and extensibility. Consider a graphical editor that supports connectivity between
objects. A user can connect two rectangles with a line, and the rectangles stay connected when the user moves either
of them. The editor ensures that the line stretches to maintain the connection.
A well-known way to maintain connectivity relationships between objects
is with a constraint-solving system. This functionality can be encapsulated in a ConstraintSolver object. ConstraintSolver
records connections as they are made and generates mathematical equations that describe them. It solves these equations
whenever the user makes a connection or otherwise modifies the diagram. ConstraintSolver uses the results of its
calculations to rearrange the graphics so that they maintain the proper connections. Supporting undo in this application
isn't as easy as it may seem. An obvious way to undo a move operation is to store the original distance moved and
move the object back an equivalent distance. However, this does not guarantee all objects will appear where they
did before. Suppose there is some slack in the connection. In that case, simply moving the rectangle back to its
original location won't necessarily achieve the desired effect.
In general, the ConstraintSolver's public interface might be insufficient to allow precise reversal of its effects
on other objects. The undo mechanism must work more closely with ConstraintSolver to reestablish previous state,
while also avoiding exposing the ConstraintSolver's internals to the undo mechanism.
The Memento
pattern can solve this problem. A Memento is an object that stores a snapshot of the internal state of another object—the
Memento's
originator. The undo mechanism will request a Memento from the originator when it needs to checkpoint the originator's state. The originator
initializes the Memento with information that characterizes its current state. Only the originator can store and
retrieve information from the Memento - the Memento is "opaque" to other objects. In the graphical editor example, the
ConstraintSolver can act as an originator. The following sequence of events characterizes the undo process:
This arrangement lets the ConstraintSolver entrust other objects with
the information it needs to revert to a previous state without exposing its internal structure and representations.