Паттерн ООП «Хранитель»
Обсудим паттерн ООП проектирования Хранитель на примере текстового редактора, который меняет форматирование текста и других элементов
10К открытий12К показов
Александр Саваткин
Lead developer в компании Alawar
«Хранитель» (Memento), также известный как Снимок – поведенческий паттерн проектирования. Он позволяет определять, сохранять, а также восстанавливать предыдущие состояния объектов без нарушения принципа инкапсуляции.
Самый простой и наглядный пример использования этого паттерна – некий текстовый редактор, который позволяет изменять форматирование текста и других элементов. Но при этом пользователь может эти изменения отменить. Другой пример – восстановление состояния персонажей в игре на контрольных точках.
Формально, в виде диаграмм структуру паттерна можно представить так:
Участники процесса:
- Memento («хранитель») – хранитель, сохраняет состояние объекта Originator;
- Originator («создатель») – создает экземпляр объекта хранителя. Имеет полный доступ к Memento;
- Caretaker («опекун») – производит сохранения состояний.
Теперь рассмотрим очень упрощённый пример текстового редактора. У него будет всего пять команд:
- добавление нового блока текста;
- установка стиля текста;
- вывод всего текста на экран;
- сохранение текущего состояния документа;
- отмена последнего действия по редактированию документа.
Для начала создадим класс нашего документа – класс Doc. Он будет содержать в себе два параметра – текст и стиль, которые пользователь может изменить с помощью соответствующих методов – AddBlock(string text) и SetStyle(int style). Выводить содержимое будем через метод Print().
Далее нам нужно создать класс для хранения состояния документа – DocMemento.
В данном случае это своего рода контейнер-копия сохранённого состояния объекта Doc. Мы передаём не копию экземпляра документа, а только его состояние со значимыми параметрами.
Теперь снова вернёмся к классу Doc и добавим два метода: для сохранения в объект-memento и восстановления состояния из объекта-memento:
Создадим класс, который будет в себе содержать историю изменений документа – EditorHistory. Вся история состояний будет храниться в стеке, который будет скрыт от пользователя для доступа напрямую.
Всё готово, теперь можно создать класс редактора и наполнить пользовательскими действиями:
Для примера мы сделали сохранение состояния после ввода блока текста «Привет, мир!» и смены параметра стиля текста. Сохранили в объекте history, снова изменили документ и вернули прежнее состояние. Каждое изменение сопроводили выводом всего документа на экран, то есть в консоль.
Если сравнивать с представленной в самом начале схемой, то в роли Originator у нас выступает Doc, Memento – DocMemento, а в роли Caretaker – EditorHistory. Документу доступны все поля, поэтому именно он делает снимок. А из истории берёт состояние для восстановления.
Данный пример слишком простой. Добавляя новые структуры и объекты в наш редактор, мы будем усложнять состояние документа (добавятся значения для разных текстовых блоков, страниц и абзацев, геометрические объекты, рисунки и тому подобное). Поэтому для более удобного представления состояния документа нам придётся использовать отдельные классы контейнеров данных, которые будут содержать множество полей. Таким образом, в более сложных программах для хранения состояний может потребоваться много памяти, если снимков будет много – это, пожалуй, основной недостаток паттерна.
Есть и чуть более сложные вариации реализации данного паттерна – создавая пустой промежуточный интерфейс или же более широкий вариант, с возможностью иметь множество видов создателей и снимков. Последний, например, позволяет полностью исключить доступ к состоянию создателей и снимков, но при этом сам опекун становится независимым от создателей.
Очень часто паттерн «Хранитель» совместно используется с паттерном «Команда» (как раз для выполнения команд «Сохранить» и «Восстановить»).
Итого, «Хранитель» позволяет нам передавать сохраняемые состояния объекту, но не передавать ему управление самим сохраняемым объектом, сохраняя инкапсуляцию.
10К открытий12К показов