Design Pattern: Memento Pattern in TypeScript

Memento pattern maintains a list of state changes performed on an object. This way, we can revert back and can go back to any point of object state history. The internal state is encapsulated inside the object, and the undo process does not need to be aware of it.

This article demonstrates Memento pattern implementations in TypeScript. Check the implementation process and examples.

Implementation

Follow the steps below to implement the Memento pattern in TypeScript-

  • Create a memento class. This can be a pre-existing class. Define some states as per requirement and define the get methods for the states.
  • Define an originator class. This class also has the same state as memento, so in some way the same reflects the memento states. Define method to create a new “Memento” object.
  • Create a caretaker class. Maintain a list of memento objects(for history). Define methods for adding/removing/accessing elements from the list.
  • For using the implementation, create a caretaker and originator object. Use the originator to generate a new memento object, then pass the memento object to the caretaker. We can then use the caretaker to move through the history of the memento object.

Examples

Let’s take a look at a few examples of Memento pattern in TypeScript.

Example #1: General Memento

Here is a demonstration of general memento pattern implementation.

Memento

  • Create file “memento.ts”.
  • Create class “Memento”.
  • Define property “state” of type string. There can be multiple elements to reflect the object property. Define states as per the requirement.
  • In the constructor take the “state” as param and set the property value.
  • Define method to get the state.
// memento.ts

class Memento {
    private state: string;

    constructor(state: string) {
        this.state = state;
    }

    getState(): string {
        return this.state;
    }
}

export default Memento;

Originator

  • Create file “originator.ts”.
  • Create class “Originator”.
  • Define state that matches with the memento.
  • Define getter and setter for state properties.
  • Define method “setMemento”, which creates a new “Memento” object and returns that.
// originator.ts

import Memento from "./memento";

class Originator {
    private state: string = '';
    
    public setState(state: string): void {
        this.state = state;
    }

    public getState(): string {
        return this.state;
    }

    public setMemento(): Memento {
        console.log("Memento Saved with timestamp => " + this.state);
        return new Memento(this.state);
    }

    public getMementoState(memento: Memento): void {
        this.state = memento.getState();
    }
}

export default Originator;

Caretaker

  • Create file “caretaker.ts”.
  • Create “Caretaker” class.
  • Define the property of type of an array of “Memento”.
  • Define methods to add/remove/access element from the “mementoList”.
  • Create a method for undoing the last change.
// caretaker.ts

import Memento from "./memento";

class Caretaker {

    private mementoList: Memento[] = [];

    add(memento: Memento): void {
        this.mementoList.push(memento);
    }

    getByIndex(index: number): Memento {
        return this.mementoList[index];
    }

    getCurrent(): Memento {
        return this.mementoList[this.mementoList.length - 1];
    }

    undo(): void {
        this.mementoList.splice(-1, 1);
    }
}

export default Caretaker;

Demo

Create caretaker and originator objects. Use the originator to generate a “Memento” object.

Pass the “Memento” object to the caretaker. Then we can use the caretaker to go back and forth in history of the memento object.

// demo.ts

import Caretaker from "./caretaker";
import Originator from "./originator";


const caretaker = new Caretaker();
const originator = new Originator();

originator.setState("Time - 1 : " + Date.now());
caretaker.add(originator.setMemento());

// Add delay if required for testing

originator.setState("Time - 2 : " + Date.now());
caretaker.add(originator.setMemento());

// Add delay if required for testing

originator.setState("Time - 3 : " + Date.now());
caretaker.add(originator.setMemento());

console.log("---------------------------------------------");

console.log("Check state at index 1 (index starts at 0):");

const stateAtIndex1 = caretaker.getByIndex(1);
console.log(stateAtIndex1.getState());

console.log("---------------------------------------------");

console.log("Check last state:");

const lastState = caretaker.getCurrent();
console.log(lastState.getState());

console.log("---------------------------------------------");

console.log("Undoing last state");

caretaker.undo();

console.log("---------------------------------------------");

console.log("Check last state after undo:");

const lastStateAfterUndo = caretaker.getCurrent();
console.log(lastStateAfterUndo.getState());

Output

Following output will be generated-

Memento Saved with timestamp => Time - 1 : 1689909369587
Memento Saved with timestamp => Time - 2 : 1689909369588
Memento Saved with timestamp => Time - 3 : 1689909369589

---------------------------------------------

Check state at index 1 (index starts at 0):
Time - 2 : 1689909369588

---------------------------------------------

Check last state:
Time - 3 : 1689909369589

---------------------------------------------

Undoing last state

---------------------------------------------

Check last state after undo:
Time - 2 : 1689909369588

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check the Memento pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.