TypeScript Memento Pattern Example

1. Definition

The Memento Design Pattern provides a mechanism to capture an object's internal state in such a way that the object can be restored to that state at a later time. This pattern involves two main actors - the Originator and the Caretaker. The Originator is the one whose state needs to be saved, and the Caretaker is the one that keeps the saved states without actually working on them.

2. Problem Statement

Imagine working with an application, like a text editor, where you wish to undo an action. A simple way might be to save instances of the editor's state for every action, but that would consume a lot of memory.

3. Solution

The Memento pattern solves this problem by offering a way to store previous states of an object (snapshots) which can then be restored at a later point. This ensures that the object itself isn't cluttered with state storage responsibilities, thus promoting a single responsibility principle.

4. Real-World Use Cases

1. Undo and redo functionality in software applications.

2. Saving game states in video games.

3. Taking snapshots of system configurations.

5. Implementation Steps

1. Create the Memento class which will store the internal state of the Originator.

2. Define the Originator class. It creates a memento containing a snapshot of its current internal state and uses the memento to restore its internal state.

3. Define the Caretaker, which stores and restores the Originator's mementos.

6. Implementation in TypeScript

// Step 1: Memento class
class Memento {
    private state: string;
    constructor(state: string) {
        this.state = state;
    }
    getState(): string {
        return this.state;
    }
}
// Step 2: Originator
class Originator {
    private state: string;
    setState(state: string): void {
        this.state = state;
    }
    getState(): string {
        return this.state;
    }
    saveStateToMemento(): Memento {
        return new Memento(this.state);
    }
    getStateFromMemento(memento: Memento): void {
        this.state = memento.getState();
    }
}
// Step 3: Caretaker
class Caretaker {
    private mementoList: Memento[] = [];
    add(memento: Memento): void {
        this.mementoList.push(memento);
    }
    get(index: number): Memento {
        return this.mementoList[index];
    }
}
// Client code
const originator = new Originator();
const caretaker = new Caretaker();
// Save states
originator.setState("State #1");
caretaker.add(originator.saveStateToMemento());
originator.setState("State #2");
caretaker.add(originator.saveStateToMemento());
// Restore a saved state
originator.getStateFromMemento(caretaker.get(0));
const restoredState = originator.getState();

Output:

State #1

Explanation:

The Originator first saves two states. Using the Caretaker, we saved these states in mementos. Later, we restore the state from the first memento and fetch it. As you can see from the output, we've successfully retrieved the state saved in the first memento.

7. When to use?

Use the Memento pattern when:

1. You need to implement a snapshot of an object's state so that it can be restored to that state later.

2. Direct exposure of the object's implementation would violate its encapsulation.

3. You want to keep a history of the states of an object.


Comments