Friday, June 17, 2022

Design Pattern: Memento Pattern

Chapters

Memento Pattern

Memento pattern is a design pattern that exposes the private internal state of an object. One example of how this can be used is to restore an object to its previous state (undo via rollback), another is versioning, another is custom serialization.

This pattern consists of three parts: Originator, Caretaker and Memento. Originator is an object with an internal state. Caretaker is an object that retrieves states from the originator and handles them. Memento is an object that contains a state of Originator. Take note that when implementing this pattern, the Originator must be the only one that can retrieve the state in a Memento.

This diagram shows how to implement a memento pattern Diagram
Courtesy of Wikipedia

This example demonstrates memento pattern.
import java.util.ArrayDeque;

public class ClientCode{

  public static void main(String[] args){
    Caretaker stateManager = 
    new Caretaker("A");
    
    System.out.println("State: " + 
    stateManager.getValue());
    stateManager.saveState();
    
    stateManager.append("B");
    stateManager.saveState();
    System.out.println("State: " + 
    stateManager.getValue());
    
    stateManager.append("C");
    stateManager.saveState();
    System.out.println("State: " + 
    stateManager.getValue());
    System.out.println();
    
    System.out.println("Undo...");
    stateManager.loadState();
    System.out.println("State: " + 
    stateManager.getValue());
    
    stateManager.loadState();
    System.out.println("State: " + 
    stateManager.getValue());
    
    stateManager.loadState();
    System.out.println("State: " + 
    stateManager.getValue());
  }
}

//Originator can have other members
//that is not part of the state of
//this class
class Originator{
  private StringBuilder state;
  
  Originator(String value){
    state = new StringBuilder(value);
  }
  
  void append(String value){
    state.append(value);
  }
  
  String getValue(){
    return state.toString();
  }
  
  Memento saveState(){
    return new Memento(
    new StringBuilder(state.toString()));
  }
  
  void loadState(Memento memento){
    state = memento.getSavedState();
  }
  
  public static class Memento{
  private final StringBuilder savedState;
    
    Memento(StringBuilder state){
      savedState = state;
    }
    
    //Making this private ensures that
    //only the Originater can retrieve
    //the state in a memento
    private StringBuilder getSavedState(){
      return savedState;
    }
  }
  
}

class Caretaker{
  private Originator originator;
  private ArrayDeque<Originator.Memento> states;
  
  public Caretaker(String value){
    originator = new Originator(value);
    states = new ArrayDeque<>();
  }
  
  public void append(String value){
    originator.append(value);
  }
  
  public String getValue(){
    return originator.getValue();
  }
  
  public void saveState(){
    states.addFirst(originator.saveState());
  }
  
  public void loadState(){
    if(states.isEmpty()){
      System.out.println("No states!");
      return;
    }
    originator.loadState(states.removeFirst());
  }
  
}

Result
State: A
State: AB
State: ABC

Undo...
State: ABC
State: AB
State: A

No comments:

Post a Comment