Memento Pattern

Posted by Dustin Boston in .

The Memento Pattern is a behavioral design pattern that allows capturing and restoring an object’s state, enabling undo functionality and preserving encapsulation.


The Memento Pattern is a behavioral design pattern that provides a way to
capture and restore an object’s state without exposing its internal details.
This pattern is particularly useful for implementing undo functionality and
maintaining encapsulation in software design.

Source Code Listing

code.ts

class Memento<T> {
  constructor(private readonly state: T) {}

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

class Originator<T> {
  constructor(private state: T) {}

  setMemento(memento: Memento<T>): void {
    this.state = memento.getState();
  }

  createMemento(): Memento<T> {
    return new Memento<T>(this.state);
  }

  _changeState(state: T): void {
    this.state = state;
  }

  _showState(): void {
    console.log(this.state);
  }
}

class Caretaker<T> {
  private memento: Memento<T> | undefined; // Make memento optional

  constructor(private readonly originator: Originator<T>) {}

  doCommand(): void {
    this.memento = this.originator.createMemento(); // Store the memento
    // ... do something that changes the originator's state...
  }

  undoCommand(): void {
    if (this.memento) {
      // Check if a memento exists
      this.originator.setMemento(this.memento);
    } else {
      console.log("Cannot undo, no memento stored.");
    }
  }
}

const client = {
  run(): void {
    const originator = new Originator<string>("foo"); // Type the originator
    const caretaker = new Caretaker<string>(originator);

    originator._showState(); // Output: foo

    caretaker.doCommand(); // Save "foo" memento
    originator._changeState("bar");
    originator._showState(); // Output: bar

    caretaker.undoCommand();
    originator._showState(); // Output: foo

    caretaker.doCommand(); // Save "bar" memento
    originator._changeState("baz");
    originator._showState(); // Output: baz

    caretaker.undoCommand();
    originator._showState(); // Output: bar

    caretaker.undoCommand(); // Call undo again
    originator._showState(); // Output: bar (still bar, because "foo" memento is gone)
  },
};

client.run();