Friday, January 31, 2014

Behavioral DP:Memento Design Pattern



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:
  1. Originator - the object that knows how to save itself.
  2. Caretaker - the object that knows why and when the Originator needs to save and restore itself.
  3. 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:       }  

Memento class for object storing.
 
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:
  1. Serialization.
  2. A class declared in the same package.
  3. 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: