Как паттерн Memento помогает реализовать undo и redo?
Поведенческие паттерны
Memento сохраняет снимок внутреннего состояния объекта, не нарушая инкапсуляцию: снимок создаёт и применяет сам объект (Originator), а хранитель (Caretaker) держит снимки, не заглядывая внутрь.
Роли
- Originator —
save()возвращает снимок,restore(m)откатывается к нему. - Memento — непрозрачный неизменяемый снимок.
- Caretaker — стеки undo и redo.
Undo/redo на двух стеках
- Перед изменением снимок кладётся в
undo, стекredoочищается. - Undo: текущее состояние →
redo, восстановление изundo.pop(). - Redo — зеркально.
class Editor {
#text = '';
#cursor = 0;
type(s) { this.#text += s; this.#cursor = this.#text.length; }
save() { return Object.freeze({ text: this.#text, cursor: this.#cursor }); }
restore(m) { this.#text = m.text; this.#cursor = m.cursor; }
get value() { return this.#text; }
}
const editor = new Editor();
const undo = [];
undo.push(editor.save()); editor.type('Привет');
undo.push(editor.save()); editor.type(', мир');
editor.restore(undo.pop());
console.log(editor.value); // ПриветЦена и альтернативы
- Полные снимки дороги: 10 МБ × 100 шагов = 1 ГБ. Спасают лимит истории, дельты и structural sharing (как в Immer).
- Подход через Command хранит обратные операции вместо состояний — памяти меньше, но у каждой команды должен быть корректный
undo().
Коротко для собеседования
Memento позволяет откатить объект к снимку, при этом снимок создаёт и читает только сам объект, а хранитель просто держит стеки undo и redo. Главная цена — память на полные снимки, её снижают лимитом истории, дельтами или хранением обратных команд.