Паттерн «Хранитель» (Memento) в Python: сохранение и восстановление состояний объекта
Паттерн «Хранитель» (Memento) в Python: сохранение и восстановление состояний объекта
Паттерн «Хранитель» (Memento) — это поведенческий паттерн проектирования, который позволяет сохранять и восстанавливать предыдущие состояния объекта, не раскрывая деталей его реализации. Он особенно полезен в сценариях, где требуется реализовать механизмы отмены операций (undo/redo), сохранения состояний системы или восстановления после ошибок.
Проблема
Представьте, что вы разрабатываете текстовый редактор. Пользователи часто хотят отменять изменения или возвращаться к предыдущим версиям текста. Если объект редактора хранит все свои данные в открытом виде, прямое сохранение его состояния может нарушить инкапсуляцию. Кроме того, сам объект не должен отвечать за управление историей своих состояний — это усложнит его код.
Решение: паттерн Memento
Паттерн предлагает вынести сохранение состояния в отдельный объект-снимок (Memento). Объект, состояние которого сохраняется (Originator), может создавать снимки и восстанавливаться из них. Управление историей снимков делегируется объекту Caretaker, который решает, когда создавать или восстанавливать снимки.
Структура паттерна:
- Originator — создает снимки своего состояния и восстанавливает его из снимков.
- Memento — хранит состояние Originator. Доступ к данным снимка имеет только Originator.
- Caretaker — управляет историей снимков (сохраняет, извлекает, удаляет).
Пример реализации на Python
Рассмотрим пример текстового редактора с функцией отмены изменений.
class EditorMemento:
"""Memento: хранит состояние редактора."""
def __init__(self, content: str, cursor_position: int):
self._content = content
self._cursor_position = cursor_position
def get_state(self) -> tuple:
"""Метод для восстановления состояния (доступен только Originator)."""
return (self._content, self._cursor_position)
class Editor:
"""Originator: создает и восстанавливает снимки."""
def __init__(self):
self._content = ""
self._cursor_position = 0
def type_text(self, text: str) -> None:
self._content += text
self._cursor_position += len(text)
def save(self) -> EditorMemento:
return EditorMemento(self._content, self._cursor_position)
def restore(self, memento: EditorMemento) -> None:
state = memento.get_state()
self._content = state[0]
self._cursor_position = state[1]
def __str__(self) -> str:
return f"Content: {self._content}, Cursor: {self._cursor_position}"
class History:
"""Caretaker: управляет историей снимков."""
def __init__(self):
self._mementos = []
def push(self, memento: EditorMemento) -> None:
self._mementos.append(memento)
def pop(self) -> EditorMemento:
return self._mementos.pop()
# Пример использования
if __name__ == "__main__":
editor = Editor()
history = History()
# Пользователь вводит текст
editor.type_text("Hello, ")
history.push(editor.save()) # Сохраняем состояние
editor.type_text("world!")
print(editor) # Content: Hello, world!, Cursor: 12
# Отмена последнего действия
editor.restore(history.pop())
print(editor) # Content: Hello, , Cursor: 7
Преимущества и недостатки
Плюсы:
- Сохраняет инкапсуляцию: состояние объекта не раскрывается внешним компонентам.
- Упрощает код Originator, делегируя управление историей Caretaker.
- Позволяет реализовать неограниченную отмену операций.
Минусы:
- Может потреблять много памяти при большом количестве снимков.
- Увеличивает сложность кода из-за добавления новых классов.
Когда использовать?
- Отмена/повтор действий (текстовые редакторы, графические программы).
- Сохранение состояний в играх (чекпоинты, сохранения).
- Транзакции в базах данных (откат изменений при ошибке).
Заключение
Паттерн Memento предоставляет элегантный способ работы с историей состояний объекта. В Python его можно реализовать с помощью классов, сохраняющих данные в приватных атрибутах, что гарантирует соблюдение инкапсуляции. Используйте этот паттерн, когда вам нужна надежная система отмены операций или контроль версий состояний.