Паттерн «Хранитель» (Memento) в Python: сохранение и восстановление состояний объекта

Паттерн «Хранитель» (Memento) в Python: сохранение и восстановление состояний объекта


Паттерн «Хранитель» (Memento) в Python: сохранение и восстановление состояний объекта

Паттерн «Хранитель» (Memento) — это поведенческий паттерн проектирования, который позволяет сохранять и восстанавливать предыдущие состояния объекта, не раскрывая деталей его реализации. Он особенно полезен в сценариях, где требуется реализовать механизмы отмены операций (undo/redo), сохранения состояний системы или восстановления после ошибок.


Проблема

Представьте, что вы разрабатываете текстовый редактор. Пользователи часто хотят отменять изменения или возвращаться к предыдущим версиям текста. Если объект редактора хранит все свои данные в открытом виде, прямое сохранение его состояния может нарушить инкапсуляцию. Кроме того, сам объект не должен отвечать за управление историей своих состояний — это усложнит его код.


Решение: паттерн Memento

Паттерн предлагает вынести сохранение состояния в отдельный объект-снимок (Memento). Объект, состояние которого сохраняется (Originator), может создавать снимки и восстанавливаться из них. Управление историей снимков делегируется объекту Caretaker, который решает, когда создавать или восстанавливать снимки.

Структура паттерна:

  1. Originator — создает снимки своего состояния и восстанавливает его из снимков.
  2. Memento — хранит состояние Originator. Доступ к данным снимка имеет только Originator.
  3. 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 его можно реализовать с помощью классов, сохраняющих данные в приватных атрибутах, что гарантирует соблюдение инкапсуляции. Используйте этот паттерн, когда вам нужна надежная система отмены операций или контроль версий состояний.