Memento Design Pattern in JavaScript

1. Definition

The Memento Design Pattern provides a mechanism to capture an object's internal state so that the object can be returned to that state at a later time. This is done without exposing the actual representation of the state.

2. Problem Statement

Sometimes we need to implement a "undo" functionality in our applications or save the state of an object to restore it later. Directly exposing the internal structure of an object to perform these tasks may violate encapsulation.

3. Solution

Introduce three main roles:

1. Originator: The object whose state we want to save and restore.

2. Memento: The object that stores the snapshot of the Originator's state.

3. Caretaker: The object that keeps track of multiple states of the Originator using Mementos.

4. Real-World Use Cases

1. Undo/Redo functionality in software like text editors or graphic design tools.

2. Game save and load system where a player's progress is stored and can be reloaded later.

3. Snapshot system in virtual machines to revert to a specific point in time.

5. Implementation Steps

1. Create the Memento class that will store the state of the Originator.

2. In the Originator class, implement methods to save its state to a Memento and restore its state from a Memento.

3. The Caretaker will hold and manage the saved states but will not directly modify or even look at them.

6. Implementation in JavaScript

// Memento Class
class Memento {
    constructor(state) {
        this._state = state;
    }
    getState() {
        return this._state;
    }
}
// Originator Class
class Originator {
    constructor() {
        this._state = '';
    }
    setState(state) {
        this._state = state;
    }
    saveToMemento() {
        return new Memento(this._state);
    }
    restoreFromMemento(memento) {
        this._state = memento.getState();
    }
    getState() {
        return this._state;
    }
}
// Caretaker Class
class Caretaker {
    constructor() {
        this._mementos = [];
    }
    addMemento(memento) {
        this._mementos.push(memento);
    }
    getMemento(index) {
        return this._mementos[index];
    }
}
// Client Code
const originator = new Originator();
const caretaker = new Caretaker();
originator.setState("State 1");
caretaker.addMemento(originator.saveToMemento());
originator.setState("State 2");
caretaker.addMemento(originator.saveToMemento());
// Restore to State 1
originator.restoreFromMemento(caretaker.getMemento(0));
console.log(originator.getState());

Output:

State 1

Explanation:

1. The Memento class stores the state of the Originator.

2. The Originator can save its state to a new Memento and can also restore its state from a given Memento.

3. The Caretaker class manages a list of mementos but doesn't interact with their contents.

4. In the client code, we changed the state of the originator twice, saving its state after each change. Later, we restored the originator's state to its first state using the memento saved by the caretaker.

7. When to use?

The Memento Pattern is useful when:

1. You need to provide a snapshot or undo mechanism without exposing an object's internal structure.

2. The internal state of an object must be saved externally so that it can be restored to that state later.

3. Direct access to the object's get and set methods would expose details and violate encapsulation.


Comments