Memento Pattern

We've talked about an undo support implementation in the previous section on the Command Pattern, and found it was not easy to implement the mechanism purely based on reversing all the operations. However, if we take snapshots of objects as their history, we may manage to avoid accumulating mistakes and make the system more stable. But then we have a problem: we need to store the states of objects while the states are encapsulated with objects themselves.

Memento Pattern helps in this situation. While a memento carries the state of an object at a certain time point, it also controls the process of setting the state back to an object. This makes the internal state implementation hidden from the undo mechanism in the following example:

Memento Pattern

We have the instances of the memento controlling the state restoration in the preceding structure. It can also be controlled by the caretaker, namely the undo mechanism, for simple state restoring cases.

Participants

The participants of Memento Pattern include:

  • Memento: Stores the state of an object and defines method restore or other APIs for restoring the states to specific objects
  • Originator: Deals with objects that need to have their internal states stored
  • Caretaker: Manages mementos without intervening with what's inside

Pattern scope

Memento Pattern mainly does two things: it prevents the caretaker from knowing the internal state implementation and decouples the state retrieving and restoring process from states managed by the Caretaker or Originator.

When the state retrieving and restoring processes are simple, having separated mementos does not help much if you are already keeping the decoupling idea in mind.

Implementation

Start with an empty State interface and Memento class. As we do not want Caretaker to know the details about state inside an Originator or Memento, we would like to make state property of Memento private. Having restoration logic inside Memento does also help with this, and thus we need method restore. So that we don't need to expose a public interface for reading state inside a memento.

And as an object assignment in JavaScript assigns only its reference, we would like to do a quick copy for the states (assuming state objects are single-level):

interface State { } 
 
class Memento { 
  private state: State; 
 
  constructor(state: State) { 
    this.state = Object.assign({} as State, state); 
  } 
 
  restore(state: State): void { 
    Object.assign(state, this.state); 
  } 
} 

For Originator we use a getter and a setter for creating and restoring specific mementos:

class Originator { 
  state: State; 
 
  get memento(): Memento { 
    return new Memento(this.state); 
  } 
 
  set memento(memento: Memento) { 
    memento.restore(this.state); 
  } 
} 

Now the Caretaker would manage the history accumulated with mementos:

class Caretaker { 
  originator: Originator; 
  history: Memento[] = []; 
 
  save(): void { 
    this.history.push(this.originator.memento); 
  } 
 
  restore(): void { 
    this.originator.memento = this.history.shift(); 
  } 
} 

In some implementations of Memento Pattern, a getState method is provided for instances of Originator to read state from a memento. But to prevent classes other than Originator from accessing the state property, it may rely on language features like a friend modifier to restrict the access (which is not yet available in TypeScript).

Consequences

Memento Pattern makes it easier for a caretaker to manage the states of originators and it becomes possible to extend state retrieving and restoring. However, a perfect implementation that seals everything might rely on language features as we've mentioned before. Using mementos could also bring a performance cost as they usually contain redundant information in trade of stability.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.48.161