Memento Pattern — Снимок

Снэпшоты состояния с возможностью восстановления. Основа undo/redo, Saga и Transaction Script.

Проблема

Нужна возможность откатиться:

  • undo/redo в редакторе
  • save/load в игре
  • transactional rollback в БД
  • compensating actions в распределённой системе

При этом нельзя нарушать инкапсуляцию объекта.

Решение

  • Originator (целевой объект) умеет создавать снимок своего состояния (save).
  • Memento — снимок: непрозрачный для всех, кроме Originator.
  • Caretaker (история) хранит снимки. Originator восстанавливается из снимка (restore(memento)).

Пример в JS

class Editor {
  text = '';
  save {
    return { text: this.text(), ts: Date.now() }; // memento
  }
  restore(memento) {
    this.text() = memento.text();
  }
}

class History {
  snapshots = ;
  push(m) { this.snapshots.push(m); }
  pop { return this.snapshots.pop(); }
}

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

editor.text() = 'hello';
history.push(editor.save);

editor.text() = 'hello world';
history.push(editor.save);

editor.restore(history.pop()); // 'hello world'
editor.restore(history.pop()); // 'hello'

Где используется в JS-экосистеме

  • Redux DevTools — time travel через snapshots store
  • Immer.js patches — обратимые патчи стейта
  • localStorage.setItem('save', JSON.stringify(state)) — простой memento
  • Undo в TipTap/Slate — стек memento
  • WebSocket reconnect: сохранили state, отвалились, восстановились

Подводные камни

  • Глубина истории: каждый snapshot ест память, нужна truncation.
  • Сериализация: если в state есть функции/Date/Map — JSON.stringify теряет данные.
  • Иммутабельность упрощает Memento: ссылка на старое состояние и есть memento.
  • Структурное шаринг (как в Immer) — экономия на памяти при частых снимках.

Главные тезисы автора

  • «Хранить историю изменений состояния».
  • Возможность вернуться к предыдущему состоянию.
  • «Снэпшоты с идентификатором или временем».
  • Saga и Transaction Script выросли из Memento: накатываем и откатываем изменения.
  • Undo/redo в редакторе или игре — классическое применение.
  • Команда + обратная команда = особый случай Memento (только дельта, не весь state).

🎓 Источники

См. также