Memento Design Pattern
Structural: Memento Pattern
What is the Memento Design Pattern in JavaScript?
View Answer:
The objects participating in this pattern are:
Originator -- example code: Person
- implements interface to create and restore mementos of itself -- in example code: hydrate and dehydrate
- the object whose state is temporary being saved and restored
Memento -- example code: JSON representation of Person
- the internal state of the Originator object in some storage format
CareTaker -- In example code: CareTaker
- responsible for storing mementos
- just a repository; it does not make changes to mementos
Here's an example of the Memento pattern in JavaScript:
class Memento {
constructor(state) {
this._state = state;
}
get state() {
return this._state;
}
}
class Originator {
constructor(state) {
this._state = state;
}
set state(state) {
console.log(`Originator: Setting state to ${state}`);
this._state = state;
}
saveToMemento() {
console.log(`Originator: Saving to Memento.`);
return new Memento(this._state);
}
restoreFromMemento(memento) {
this._state = memento.state;
console.log(`Originator: State after restoring from Memento: ${this._state}`);
}
}
class Caretaker {
constructor() {
this._mementos = [];
}
addMemento(memento) {
this._mementos.push(memento);
}
getMemento(index) {
return this._mementos[index];
}
}
// Usage
let caretaker = new Caretaker();
let originator = new Originator('State1');
caretaker.addMemento(originator.saveToMemento());
originator.state = 'State2';
caretaker.addMemento(originator.saveToMemento());
originator.state = 'State3';
caretaker.addMemento(originator.saveToMemento());
originator.restoreFromMemento(caretaker.getMemento(1)); // Restore to State2
In this example, the Originator
is the object whose state we want to save, Memento
is the object that stores the saved state, and Caretaker
stores a list of all saved states. Whenever the state of Originator
changes, we save the state to a new Memento
and add it to Caretaker
. If we want to restore the state of Originator
, we get a Memento
from Caretaker
and restore the state from that Memento
.
Why use the Memento Pattern in JavaScript?
View Answer:
The Memento pattern belongs to which pattern category?
View Answer:
What is a suitable JavaScript use case for the Memento Pattern?
View Answer:
The Memento pattern allows you to create complete copies of an object's state, including private fields, and store them independently from the object. While most people remember this pattern because of the "undo" use case, it's also helpful when dealing with transactions (i.e., if you need to roll back an operation on an error).
We can also use this pattern when direct access to an object's fields/getters/setters violates its encapsulation. The Memento makes the object responsible for capturing a snapshot of its current state. Because no other object can read the snapshot, the original object's state data remains safe and secure.
What are some of the advantages of employing the Memento pattern?
View Answer:
- Without breaking the object's encapsulation, you can take snapshots of its state.
- You can simplify the originator's code by allowing the caretaker to keep track of the originator's state history.
What are the shortcomings of the Memento Pattern?
View Answer:
Drawbacks of the Memento Pattern.
- If clients frequently create mementos, the program may consume a large amount of RAM/memory.
- To be able to destroy outmoded keepsakes, caregivers should track the originator's lifecycle.
- Most dynamic programming languages, such as JavaScript, cannot guarantee that the Memento's state remains unchanged.
Are there any alternatives to using the Memento pattern?
View Answer:
How does the Memento Pattern differ from other design patterns?
View Answer:
What are the main components of the Memento Pattern?
View Answer:
Here is a modern JavaScript example using classes and getter/setter methods.
class Memento {
constructor(state) {
this.state = state;
}
}
class Originator {
constructor() {
this.state = null;
}
createMemento() {
return new Memento(this.state);
}
restore(memento) {
this.state = memento.state;
}
}
class Caretaker {
constructor() {
this.mementos = [];
}
addMemento(memento) {
this.mementos.push(memento);
}
getMemento(index) {
return this.mementos[index];
}
}
// Usage
let originator = new Originator();
let caretaker = new Caretaker();
originator.state = "State1";
caretaker.addMemento(originator.createMemento());
originator.state = "State2";
caretaker.addMemento(originator.createMemento());
originator.state = "State3";
caretaker.addMemento(originator.createMemento());
originator.restore(caretaker.getMemento(1)); // Restores to State2
console.log(originator.state); // Outputs: State2
In this example:
- Originator: the object that knows how to save its state in a memento and restore itself from a memento. - Memento: the object that stores the internal state of the Originator. It's a passive object that only Originator can assess. - Caretaker: the object that keeps track of multiple memento. It can store and retrieve stored memento, but cannot operate on them.