The GoF book says that the Memento pattern is designed to,
“Without violating encapsulation, capture and
externalize an object’s internal state so that the object can be restored to
this state later.”
Intent
- The intent of this pattern is to capture the internal state of an object without violating encapsulation and thus providing a mean for restoring the object into initial state when needed.
- Promote undo or rollback to full object status.
Example 1: The client code has total access to the
database, as outlined below, so if someone flubs an operation, the database is
in danger
More than just a save-state undo
command, the idea here is to “capture and externalize an object’s internal
state” for backup. You might do that with a save-state object accessible from
both the client code and the database, as shown below
That solution, however, violates the
database’s encapsulation, and the Memento design pattern starts off by saying,
“Without violating encapsulation…..” So what do you do? You make the
save-state object private to the database,
Now the save-state object is
inaccessible outside of the database — you’re not violating the database’s
encapsulation. That’s the Memento design pattern — using it, you can create
save-state objects that enable commands like undo, without violating the main
object’s encapsulation.
The
Memento design pattern defines three distinct roles:
- Originator - the object that knows how to save itself.
- Caretaker - the object that knows why and when the Originator needs to save and restore itself.
- Memento - the lock box that is written and read by the Originator, and shepherded by the Caretaker.
In Memento pattern, we do not create a copy of an
object. We create Mementos that hold the state of an object and it might
include full object or elements of an object that needs to be stored.
Ingredients of a Memento Pattern
- Originator: It is the one whose state needs to be saved & creates the Memento object.
- Memento: It holds the internal state of an Originator.
- Caretaker: It is responsible for keeping the memento.
The caretaker first asks the originator for a memento
object. Then it does whatever operation (or sequence of operations) it was
going to do. To roll back to the state before the operations, it returns the
memento object to the originator.
The memento object itself is an opaque object
(one which the caretaker cannot, or should not, change)
1: public class Originator {
2: private String state;
3: public String getState() {
4: System.out.println("Originator: Setting state to " + state);
5: return state;
6: }
7: public void setState(String state) {
8: System.out.println("Originator: Setting state to " + state);
9: this.state = state;
10: }
11: public Memento saveStateToMemento() {
12: System.out.println("Originator: Saving to Memento.");
13: return new Memento(state);
14: }
15: public void restoreFromMemento(Memento memento) {
16: state = memento.getSavedState();
17: System.out.println("Originator: State after restoring from Memento: " + state);
18: }
1: public static class Memento {
2: String state;
3: public Memento(String state) {
4: this.state = state;
5: }
6: public String getSavedState() {
7: return state;
8: }
9: public void setSavedState(String state) {
10: this.state = state;
11: }
12: }
13: }
1: import java.util.List;
2: import java.util.ArrayList;
3: public class CareTaker {
4: public static void main(String[] args) {
5: List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
6: Originator originator = new Originator();
7: originator.setState("state1");
8: originator.setState("state2");
9: savedStates.add(originator.saveStateToMemento());
10: originator.setState("State3");
11: // We can request multiple mementos, and choose which one to roll back to.
12: savedStates.add(originator.saveStateToMemento());
13: originator.setState("State4");
14: originator.restoreFromMemento(savedStates.get(1));
15: }
16: }
The output is:
Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3
This example uses a String as the state, which is an immutable object
in Java. In real-life scenarios the state will almost always be an object (in
which case the state must be cloned).
private Memento(State state)
{
//state has to be cloned before returning the
//memento, or successive calls to get Memento
//return a reference to the same object
this.mementoState = state.clone();
}
It
must be said that the latter implementation has a drawback: it declares an
internal class. (It would be better if the memento strategy could apply on more
than one object.)
There
are mainly three other ways to achieve Memento:
- Serialization.
- A class declared in the same package.
- The object can also be accessed via a proxy, which can achieve any save/restore operation on the object.
Notes :
1)
Command
and Memento act as magic tokens to
be passed around and invoked at a later time. In Command, the token represents
a request; in Memento, it represents
the internal state of an object at a particular time. Polymorphism is important to Command, but not to Memento because its interface is so narrow
that a memento can only be passed as a value
2)
Memento is often used in conjunction with
Iterator. An Iterator can use a Memento to capture the state of an iteration.
The Iterator stores the Memento internally.
Consequences
Memento protects encapsulation and avoids exposing originator's internal state and implementation. It also simplifies originator code such that the originator does not need to keep track of its previous state since this is the responsibility of the CareTaker.
Using the memento pattern can be expensive depending on the amount of
state information that has to be stored inside the memento object. In addition
the caretaker must contain additional logic to be able to manage mementos.
One of the drawbacks is that if Originator object is very
huge then Memento object size will also be huge and use a lot of memory.
No comments:
Post a Comment