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
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.
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.