Перейти к основному содержимому

Паттерн "Мементо" в Java — снимки состояния и undo

Разработчику Архитектору

Обзор — в поведенческих паттернах. Здесь — Memento на Java: сохранить снимок состояния объекта и откатиться, не открывая внутренние поля посторонним классам.

Загрузка редактора схем…

Задача паттерна

Memento фиксирует полное состояние объекта в отдельной структуре (снимок), чтобы позже восстановить его. Аналогия — Ctrl+Z в редакторе: где-то лежит копия прошлого состояния.

Три роли:

РольПример
OriginatorServerConfig — создаёт и принимает снимок
MementoConfigMemento — неизменяемые данные снимка
CaretakerConfigHistory — стек снимков, без доступа к деталям

Пример — конфигурация сервера

import java.time.LocalDateTime;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

record ConfigMemento(Map<String, String> properties, LocalDateTime timestamp) {
ConfigMemento {
properties = Map.copyOf(properties);
}
}

class ServerConfig {
private final Map<String, String> properties = new HashMap<>();

void set(String key, String value) {
properties.put(key, value);
}

String get(String key) {
return properties.get(key);
}

ConfigMemento save() {
return new ConfigMemento(properties, LocalDateTime.now());
}

void restore(ConfigMemento memento) {
properties.clear();
properties.putAll(memento.properties());
}
}

class ConfigHistory {
private final Deque<ConfigMemento> snapshots = new ArrayDeque<>();

void backup(ServerConfig config) {
snapshots.push(config.save());
}

void undo(ServerConfig config) {
if (!snapshots.isEmpty()) {
config.restore(snapshots.pop());
}
}
}
ServerConfig config = new ServerConfig();
ConfigHistory history = new ConfigHistory();

config.set("maxThreads", "200");
history.backup(config);

config.set("maxThreads", "500");
config.set("timeout", "30s");
history.backup(config);

config.set("maxThreads", "1");
history.undo(config);

System.out.println(config.get("maxThreads")); // 500

record с компактным конструктором даёт защитную копию Map — caretaker не сможет случайно изменить снимок снаружи.


Memento и Command

MementoCommand
ХранитСнимок состоянияОписание действия
Откатrestore(memento)undo() инвертирует операцию
Когда лучшеНужна точная копия всего состоянияНужны лог, очередь, сериализация команд

Практика Command на Java — 126, на C# — 120.


Когда применять и риски

Уместен для undo, checkpoint, черновиков, игровых сейвов.

Риск — память: частые снимки больших объектов. Ограничивайте глубину стека, храните дельты или сжимайте историю.


См. также

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").