Хранитель

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

Шаблон хранитель фиксирует и хранит текущее состояние объекта, чтобы оно легко восстанавливалось.

Когда использовать паттерн
  • Когда нужно сохранять промежуточные состояния объекта, чтобы можно было позже восстановить их.

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

  • Когда необходимо поддерживать инкапсуляцию, избегая раскрытия сложных данных другим классам.
Основные участники паттерна
Memento (Хранитель) — класс, представляющий сохранённое состояние объекта. Хранитель предоставляет доступ к состоянию только объекту, который его создал.

Originator (Создатель) — класс, который создаёт объекты-хранители для сохранения своего состояния и использует их для восстановления.

Caretaker (Опекун) — класс, который хранит объекты-хранители, но не знает их содержимого. Его задача — сохранять и управлять хранителями.
Пример реализации паттерна
Рассмотрим пример текстового редактора, в котором реализована функция «Отменить». Редактор сохраняет своё состояние (например, текст) в объекте-хранителе. При необходимости можно восстановить предыдущее состояние.
Шаг 1: Создаём класс хранителя
class Memento:
    def __init__(self, state):
        self._state = state

    def get_state(self):
        return self._state
Класс Memento хранит состояние текстового редактора.

Здесь Memento имеет метод get_state, возвращающий сохранённое состояние. Это состояние будет доступно только классу Originator, который его создал.
Шаг 2: Создаём класс создателя
class TextEditor:
    def __init__(self):
        self._text = ""

    def type_text(self, text):
        self._text += text

    def save(self):
        return Memento(self._text)

    def restore(self, memento):
        self._text = memento.get_state()

    def get_text(self):
        return self._text
Класс TextEditor — это объект, чьё состояние нужно сохранять и восстанавливать.

  • Метод type_text добавляет новый текст к существующему.
  • Метод save создаёт объект-хранитель, сохраняющий текущее состояние текста.
  • Метод restore восстанавливает состояние из переданного хранителя.
  • Метод get_text возвращает текущий текст редактора.
Шаг 3: Создаём класс опекуна
class Caretaker:
    def __init__(self):
        self._history = []

    def backup(self, memento):
        self._history.append(memento)

    def undo(self):
        if not self._history:
            return None
        return self._history.pop()
Класс Caretaker управляет хранением объектов-хранителей. Он хранит сохранённые состояния, не раскрывая их внутреннюю структуру.

  • Метод backup добавляет хранителя в историю.
  • Метод undo удаляет и возвращает последний сохранённый хранитель, позволяя откатить состояние.
Шаг 4: Использование паттерна
def main():
    editor = TextEditor()
    caretaker = Caretaker()

    # Набираем текст и сохраняем состояния
    editor.type_text("Hello, ")
    caretaker.backup(editor.save())
    print("Current text:", editor.get_text())  # Current text: Hello, 

    editor.type_text("world!")
    caretaker.backup(editor.save())
    print("Current text:", editor.get_text())  # Current text: Hello, world!

    # Вносим изменения и сохраняем новое состояние
    editor.type_text(" How are you?")
    print("Current text:", editor.get_text())  # Current text: Hello, world! How are you?

    # Откатываемся к предыдущему состоянию
    editor.restore(caretaker.undo())
    print("After undo:", editor.get_text())  # After undo: Hello, world!

    # Ещё одно откатывание
    editor.restore(caretaker.undo())
    print("After second undo:", editor.get_text())  # After second undo: Hello, 

main()
Результат выполнения
Current text: Hello, 
Current text: Hello, world!
Current text: Hello, world! How are you?
After undo: Hello, world!
After second undo: Hello, 
Как работает код?
  • Создание и сохранение состояния. Редактор создаёт хранителя с текущим текстом, и он сохраняется Caretaker.

  • Восстановление состояния. При вызове restore, редактор восстанавливает текст из последнего сохранённого состояния, предоставленного Caretaker.

  • Откатывание изменений. Метод undo в Caretaker позволяет откатывать изменения, восстанавливая состояние текста до нужного момента.
Преимущества паттерна
  • Сохранение инкапсуляции. Внутренние данные сохраняются и восстанавливаются без нарушения инкапсуляции, поскольку они скрыты в объекте-хранителе.

  • Простота отмены изменений. Этот паттерн облегчает добавление функции отмены (Undo), позволяя легко вернуться к предыдущим состояниям.

  • Безопасность данных. Внешний класс (Caretaker) хранит хранители, не зная деталей их содержимого.
Недостатки
  • Потребление памяти. Сохранение каждого состояния объекта может привести к увеличению потребления памяти, особенно если состояние объекта велико.

  • Сложность при частых изменениях. Многочисленные сохранения и откаты могут усложнить управление состояниями и привести к излишнему накоплению данных.