Memento

Memento Pattern

Introduction

The Memento pattern is a behavioral design pattern that allows an object to save its current state so that it can be restored to this state later. It’s particularly useful for implementing undo mechanisms or for taking snapshots of an object’s state.

Purpose

  • To capture and externalize an object’s internal state so that the object can be returned to this state later.
  • To provide a way to restore an object to its previous state without revealing the details of its implementation.

Use Cases

  • When a snapshot of an object’s state needs to be saved so that it can be restored to that state later.
  • When a direct interface to obtain the state would expose implementation details and break the object’s encapsulation.

Advantages/Disadvantages

Advantages:

  • Preserves encapsulation boundaries by keeping details of the object’s state private.
  • Simplifies the originator’s code by delegating the caretaking functions to the memento.

Disadvantages:

  • Can be costly in terms of memory if care is not taken to manage the number and size of mementos created.
  • Increases complexity by adding additional classes.

Implementation in JavaScript

javascript
	class Memento {
	  constructor(state) {
	    this.state = state;
	  }
	
	  getState() {
	    return this.state;
	  }
	}
	
	class Originator {
	  constructor() {
	    this.state = '';
	  }
	
	  setState(state) {
	    this.state = state;
	  }
	
	  getState() {
	    return this.state;
	  }
	
	  save() {
	    return new Memento(this.state);
	  }
	
	  restore(memento) {
	    this.state = memento.getState();
	  }
	}
	
	class Caretaker {
	  constructor() {
	    this.mementos = [];
	  }
	
	  addMemento(memento) {
	    this.mementos.push(memento);
	  }
	
	  getMemento(index) {
	    return this.mementos[index];
	  }
	}
	
	// Usage
	const originator = new Originator();
	const caretaker = new Caretaker();
	
	originator.setState('State #1');
	originator.setState('State #2');
	caretaker.addMemento(originator.save()); // Save state
	
	originator.setState('State #3');
	console.log('Current State:', originator.getState()); // "State #3"
	
	// Restoring the saved state
	originator.restore(caretaker.getMemento(0));
	console.log('Restored State:', originator.getState()); // "State #2"

Practical Example

Consider a backend application where you need to maintain the state of an object that represents a user session. The Memento pattern can be used to save and restore the state of the session.

javascript
	class Session {
	  constructor(state) {
	    this.state = state;
	  }
	
	  saveState() {
	    return new Memento(this.state);
	  }
	
	  restoreState(memento) {
	    this.state = memento.getState();
	  }
	
	  getState() {
	    return this.state;
	  }
	
	  setState(state) {
	    this.state = state;
	  }
	}
	
	class SessionCaretaker {
	  constructor() {
	    this.savedSessions = [];
	  }
	
	  addMemento(memento) {
	    this.savedSessions.push(memento);
	  }
	
	  getMemento(index) {
	    return this.savedSessions[index];
	  }
	}
	
	// Usage
	const session = new Session({ user: 'JohnDoe', data: 'Data 1' });
	const caretaker = new SessionCaretaker();
	
	caretaker.addMemento(session.saveState()); // Save session state
	
	session.setState({ user: 'JohnDoe', data: 'Data 2' }); // Change state
	console.log('Current Session:', session.getState());
	
	session.restoreState(caretaker.getMemento(0)); // Restore session state
	console.log('Restored Session:', session.getState());

In this example, Session is the originator that saves its state to a Memento. SessionCaretaker keeps track of these mementos, allowing the session’s state to be saved and restored as needed.

Next, we will discuss the Observer pattern.