Memento Pattern

The Memento Pattern is a Behavioral Design Pattern that allows capturing and storing an object’s state so it can be restored later without violating its encapsulation. By using the Memento Pattern, you can create a “snapshot” of an object’s current state and revert to it at a later point, enabling features like undo, history tracking, and rollback. This pattern is especially useful in applications that require managing state changes, such as text editors, graphic design tools, or games.

The Memento Pattern consists of three main components:

  1. Originator: The object whose state you want to save and restore.
  2. Memento: A snapshot of the Originator‘s state.
  3. Caretaker: The entity responsible for storing and managing mementos.

Key Characteristics of Memento Pattern

  1. State Preservation:
    • The Memento Pattern allows you to save an object’s state at specific points and restore it later, which is essential for undo functionality or history tracking.
  2. Encapsulation of State:
    • The memento object is designed to store the internal state of the originator without revealing the details to other objects, preserving encapsulation.
  3. Supports Undo/Redo Functionality:
    • By storing multiple mementos in a stack or list, the pattern supports multi-level undo and redo functionality, commonly used in applications with complex user interactions.
  4. Minimal Interface Exposure:
    • The Originator and Caretaker are the only components that directly interact with the memento, reducing the risk of unintended access to the state.
  5. Decouples State Management from Core Logic:
    • The Caretaker manages the mementos, allowing the Originator to focus on its core responsibilities without managing its history.
  6. May Consume More Memory:
    • Depending on the complexity of the state being stored, the Memento Pattern can consume significant memory, particularly in applications that frequently save snapshots.

Memento Pattern in Node.js

In Node.js, the Memento Pattern is useful in applications that need to track state changes, such as building a text editor with undo/redo functionality. Let’s illustrate the Memento Pattern with an example of a text editor that allows users to make changes, undo, and redo actions.

Example Scenario: Text Editor with Undo/Redo

In this example, we’ll create a simple text editor using the Memento Pattern. The editor will allow users to add text, undo changes, and redo changes.

Step 1: Define the Originator (TextEditor)

The TextEditor class represents the Originator, which holds the current state of the text. It includes methods to save the current state to a memento and restore a previous state from a memento.

// Originator: TextEditor
class TextEditor {
  constructor() {
    this.content = "";
  }

  // Method to type new text
  type(text) {
    this.content += text;
  }

  // Save the current state as a memento
  save() {
    return new Memento(this.content);
  }

  // Restore the state from a memento
  restore(memento) {
    this.content = memento.getContent();
  }

  // Get current content
  getContent() {
    return this.content;
  }
}

Step 2: Define the Memento

The Memento class is a simple object that stores the state of the TextEditor. It provides a way to encapsulate the state without exposing the internal details.

// Memento: stores the editor's state
class Memento {
  constructor(content) {
    this.content = content;
  }

  getContent() {
    return this.content;
  }
}

Step 3: Define the Caretaker (History)

The History class acts as the Caretaker, managing a stack of mementos for undo and redo functionality.

// Caretaker: History
class History {
  constructor() {
    this.mementos = [];
    this.currentIndex = -1;
  }

  // Add a memento to history
  push(memento) {
    // Discard redo history if new action is taken after undo
    this.mementos = this.mementos.slice(0, this.currentIndex + 1);
    this.mementos.push(memento);
    this.currentIndex++;
  }

  // Undo by retrieving the previous memento
  undo() {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      return this.mementos[this.currentIndex];
    }
    return null;
  }

  // Redo by retrieving the next memento
  redo() {
    if (this.currentIndex < this.mementos.length - 1) {
      this.currentIndex++;
      return this.mementos[this.currentIndex];
    }
    return null;
  }
}

Step 4: Using the Memento Pattern

Now, let’s bring everything together by creating a TextEditor with undo and redo capabilities.

const editor = new TextEditor();
const history = new History();

// Typing text and saving states
editor.type("Hello, ");
history.push(editor.save()); // Save state

editor.type("World!");
history.push(editor.save()); // Save state

console.log(editor.getContent()); // Output: Hello, World!

// Undo action
editor.restore(history.undo());
console.log(editor.getContent()); // Output: Hello, 

// Redo action
editor.restore(history.redo());
console.log(editor.getContent()); // Output: Hello, World!

Output:

Hello, World!
Hello, 
Hello, World!

Explanation:

  • The TextEditor class manages its content, acting as the Originator.
  • Each time the editor’s state is saved, a memento is created and stored in History.
  • The undo and redo operations allow the TextEditor to restore previous states from the history.

Real-World Examples of Memento Pattern

  1. Text Editors and IDEs:
    • Text editors (e.g., Microsoft Word, Google Docs) use the Memento Pattern to implement undo/redo functionality. Each change in the document is saved as a memento, allowing users to go back to previous versions of the document or redo changes.
  2. Graphic Design Tools:
    • Design software like Adobe Photoshop or Illustrator uses the Memento Pattern to store snapshots of the design. Designers can use undo/redo features to backtrack their changes, preserving the state of the design at specific points.
  3. Database Transactions:
    • In systems where data consistency is crucial, the Memento Pattern is used to store checkpoints or “snapshots” of data. If a transaction fails, the system can roll back to the previous state using the stored snapshot.
  4. Game Development (Save States):
    • Many games use the Memento Pattern to manage save points, allowing players to revert to a previous state of the game if they fail or wish to try a different approach. Each save point acts as a memento that stores the state of the game at that specific moment.
  5. Configuration Management:
    • In configuration management, applications can store configuration snapshots. If a configuration update leads to issues, the Memento Pattern allows the application to revert to the previous stable configuration.
  6. Web Applications with Session History:
    • In web applications, session history is managed using mementos to store the state of the app. This allows users to navigate backward and forward between states without reloading data or losing progress.
  7. Data Backup and Restore Systems:
    • Backup systems often use the Memento Pattern to store data snapshots periodically. When data corruption or accidental deletion occurs, the system can restore data from the latest backup, preserving the most recent stable state.

Conclusion

The Memento Pattern is a valuable design pattern that enables state preservation and restoration. By allowing an object’s state to be saved and restored later, this pattern supports features like undo/redo, rollback, and history tracking. It is particularly useful in applications where maintaining a history of changes or restoring previous states is essential.

In Node.js, the Memento Pattern can be applied in various scenarios, including building text editors, managing database transactions, and implementing save/restore functionality in games. The Memento Pattern promotes encapsulation by preventing direct access to an object’s internal state, instead capturing snapshots as mementos that can be stored and managed by a caretaker. By decoupling state management from core functionality, this pattern improves modularity, maintainability, and the ability to manage state in complex applications.

Leave a comment

I’m Tran Minh

Hi, I’m Trần Minh, a Solution Architect passionate about crafting innovative and efficient solutions that make technology work seamlessly for you. Whether you’re here to explore the latest in tech or just to get inspired, I hope you find something that sparks joy and curiosity. Let’s embark on this exciting journey together!

Let’s connect