Паттерн Снимок (Memento) в PHP
Короткий скринкаст: паттерн «Снимок» (Memento) — сохраняем состояние объекта, чтобы потом можно было к нему вернуться. Реализуем отмену правок текста.
Описание
Снимок позволяет сохранять и восстанавливать состояние объекта, не раскрывая его внутреннее устройство. Редактор Editor — это источник состояния: его метод save() создаёт снимок Memento с текущим текстом, а restore() восстанавливает текст из снимка. Снимки хранит отдельный объект-смотритель History: он складывает их в стек (push) и достаёт последний (pop). Клиент просит редактор сохранить снимок и передаёт его истории — сам редактор не знает, где и сколько снимков лежит, а снимок снаружи доступен только для чтения.
UML-диаграмма
Код
Memento.php
class Memento
{
public function __construct(private readonly string $text) {}
public function getText(): string
{
return $this->text;
}
}
Editor.php
class Editor
{
private string $text = '';
public function setText(string $text): void
{
$this->text = $text;
}
public function getText(): string
{
return $this->text;
}
public function save(): Memento
{
return new Memento($this->text);
}
public function restore(Memento $memento): void
{
$this->text = $memento->getText();
}
}
History.php
class History
{
private array $mementos = [];
public function push(Memento $memento): void
{
$this->mementos[] = $memento;
}
public function pop(): ?Memento
{
return array_pop($this->mementos);
}
}
demo.php
require_once 'Memento.php';
require_once 'Editor.php';
require_once 'History.php';
$editor = new Editor;
$history = new History;
$editor->setText('Version 1');
$history->push($editor->save());
$editor->setText('Version 2');
$history->push($editor->save());
$editor->setText('Version 3');
echo $editor->getText().PHP_EOL;
$editor->restore($history->pop());
echo $editor->getText().PHP_EOL;
Пример результата
После php demo.php видна версия 3, а затем откат к версии 2 из снимка:
Version 3
Version 2
Преимущества
- Состояние можно сохранять и откатывать, не нарушая инкапсуляцию объекта.
- Снимок неизменяем (
readonly), поэтому хранить его безопасно. - Логика хранения истории вынесена в отдельный объект-смотритель.
- Упрощает реализацию отмены, истории и транзакций.
Когда использовать
- Когда нужна отмена или откат состояния объекта.
- Когда надо сохранить историю изменений и вернуться к прошлому снимку.
- Когда прямое копирование полей объекта нарушило бы инкапсуляцию.
- Когда снимок должен оставаться неизменным после создания.
Использование в Laravel
В Laravel Снимок встречается там, где важно сохранить состояние для отката. Eloquent хранит исходные значения атрибутов модели и методы getOriginal(), getChanges() и isDirty() позволяют вернуться к состоянию до сохранения. Миграции используют снимки схемы для rollback, а Illuminate\Support\Facades\Cache сохраняет готовые значения, которые позже восстанавливаются. Ближе всего к классическому снимку — транзакции БД с откатом: DB::beginTransaction() и DB::rollBack() возвращают данные к точке сохранения.