Казахстан, Караганды, Карагандинский экономический университет, 2009 |
Поведенческие шаблоны проектирования
Введение
Поведенческие шаблоны проектирования определяют общие закономерности связей между объектами, реализующими данные паттерны. Следование этим шаблонам уменьшает связность системы и оптимизирует взаимодействие между объектами, что приводит к улучшению гибкости, надежности и сопровождаемости программного продукта.
Поведенческие шаблоны, как и все паттерны проектирования в целом, составляют собой библиотеку практических приемов, подкрепленных теоретическим базисом сферы информационных технологий. Они не являются абстрактным знанием, а служат утилитарным целям и могут приносить достаточную ценность при их последовательном, постоянном и грамотном применении.
Шаблоны проектирования поведения объектов
В поведенческих шаблонах, как и в смежных им структурных шаблонах, используется наследование, в качестве инструмента определения поведения для различных классов, и композиция, как уравнитель и распределитель выполняемых ими обязанностей.
Некоторые из тех, что мы рассмотрим далее, описывают, как с помощью взаимодействия наборы равноправных объектов работают над заданием, которое они не могут выполнить по отдельности. Ключевым моментом в таком подходе является осведомленность объектов о существовании друг друга. "Коллеги" должны хранить ссылки друг на друга, но это усиливает степень связанности системы. При высокой связанности каждому объекту пришлось бы иметь информацию обо всех остальных.
Поведенческие шаблоны проектирования решают задачи, обозначенные выше, и оптимизируют общую производительность системы.
Интерпретатор
Когда необходимо разрабатывать и поддерживать одну и ту же часто встречающуюся, подверженную изменениям задачу, это приводит к тому, что сопровождать логику приложения с большим количеством изменений затруднительно.
В этом случае целесообразно разработать шаблон "Интерпретатор", который решает данную задачу.
"Интерпретатор" (англ. Interpreter) – поведенческий шаблон проектирования, решающий часто встречающуюся, но подверженную изменениям задачу. Также известен как Little (Small) Language.
Как уже было обозначено, данный шаблон проектирования применяется для решения задач часто повторяющихся операций. Несмотря на то, что реализация этого паттерна требует понимания теории формальных языков и грамматик, он не сложен в восприятии и дальнейшей трактовки. Для его разработки потребуются следующие участники:
- Абстрактное выражение.
- Определяет интерфейс выражения, объявляет метод шаблона.
- Терминальное выражение.
- Реализует методы. Для каждого объекта создается свое терминальное выражение.
- Нетерминальное выражение.
- Представляет правило. Для каждого отдельного правила создается свой объект нетерминального выражения.
- Контекст.
- Содержит общую информацию. Может использоваться объектами терминальных и нетерминальных выражений для сохранения состояния операций и последующего доступа к сохраненному состоянию.
- Клиент.
- Управляет выполнением бизнес-логики в виде абстрактного синтаксического дерева, узлами которого являются объекты терминального и нетерминального выражения.
- Методы "Интерпретатора" в нетерминальных выражениях позволяют реализовать правила. При этом мы легко можем добавить новые правила, определив новые объекты нетерминального выражения со своей реализацией метода шаблона. Однако недостаток данного шаблона заключается в том, что он подходит только для тех случаев, когда правила относительно просты и не содержат большого количества ответвлений. В более сложных случаях следует выбирать другие способы проектирования приложения.
Жизненной аналогией этого шаблона является мясорубка, которая способна по определенным правилам преобразовывать небольшой входной набор кусков различного мяса к определенному виду фарша (разного состава). Такая мясорубка является достаточно надежным и универсальным инструментом, если на вход ей не подавать что-то совсем плотное и жесткое.
Итератор
В тех случаях, когда требуется, чтобы сложный составной объект, например список, предоставлял доступ к своим элементам (объектам), не раскрывая их внутреннюю структуру, причем перебирать список требуется по-разному в зависимости от задачи, применяется шаблон "Итератор".
В качестве основного назначения паттерна следует выделить:
- предоставление способа последовательного доступа ко всем элементам составного объекта, не раскрывая его внутреннего представления.
- Реализация шаблона должна иметь абстракцию, позволяющую разделить классы коллекций и алгоритмов.
Таким образом, данный паттерн используется, когда необходим механизм "абстрактного" обхода различных структур данных так, чтобы были определены алгоритмы, способные взаимодействовать со структурами прозрачно. Если раскрыть тему реализации и возможностей этого шаблона, то следует сказать о том, что любой составной объект, такой как список, должен предоставлять способ доступа к его элементам без раскрытия своей внутренней структуры. Иногда это является не просто вариантом использования структуры данных, а нужно перебирать элементы списка различными способами, в зависимости от конкретной задачи, а иногда нужно иметь несколько активных обходов одного списка одновременно. Идеальным решением в таком случае является единый интерфейс для обхода разных типов составных объектов. Именно подобная функциональность работы с комплексными объектами осуществляется с помощью паттерна "Итератор". Ключевая идея состоит в том, чтобы ответственность за доступ и обход переместить из составного объекта на сам итератор, который будет определять стандартный протокол обхода.
Алгоритм реализации данного шаблона предписывает следующие стадии:
- Создается определенный класс ("Итератор"), который определяет интерфейс для доступа и перебора элементов.
- Конкретный экземпляр класса "Итератор" реализует его интерфейс и следит за текущей позицией при обходе агрегата.
- Агрегат определяет интерфейс для создания объекта-итератора.
- Конкретный экземпляр агрегата реализует интерфейс создания итератора и возвращает экземпляр его класса.
- Конкретный итератор отслеживает текущий объект в агрегате и может вычислить следующий объект при переборе.
Таким образом, из коллекции данных "выносится" функциональность обхода элементов и ей придается статус объекта. Это приводит к тому, что:
- упрощается коллекция данных;
- появляется возможность одновременно создавать множество активных обходов;
- алгоритмы обработки становятся отделены от структур данных.
Каждому классу рекомендуется иметь итератор. В том случае, когда потребуется дополнительная функциональность, если "Итератор" первоначально был предусмотрен, то можно будет добавить необходимую функциональность достаточно простои быстро, без изменений первоначального кода программы.
Аналогией этого шаблона является алфавитный библиотечный указатель, который предоставляет своему потребителю возможность быстро изучить структуру его элементов и при необходимости получить к ним доступ, но не предоставляет сами элементы, а только ссылки на них.
Команда (Транзакция)
Когда необходимо послать объекту запрос, не зная о том, выполнение какой операции запрошено и кто будет получателем, целесообразно применять шаблон "Команда". Основополагающая идея данного шаблона заключается в использовании единого интерфейса для описания всех типов операций, которые можно производить с системой. Для добавления в систему поддержки новой операции достаточно реализовать требуемый интерфейс. Каждая операция представляется самостоятельным объектом, инкапсулирующим некоторый набор дополнительных свойств.
В этом шаблоне алгоритм представления бизнес-логики организован в виде последовательности процедур, которые управляют каждая своим запросом. Любое приложение можно представить в виде набора транзакций. Какие-то из них выбирают данные, какие-то – меняют. Каждое взаимодействие пользователя и системы содержит определенный набор действий. Паттерн "Команда" организует всю используемую логику в одну "над" -процедуру, работая сданными напрямую или через тонкую обертку.
При реализации шаблона "Команда" следует обратить внимание на следующие моменты:
- Насколько "умной" должна быть "Команда". У "Команды" может быть широкий круг обязанностей. С одной стороны, простое определение связи между получателем и действиями, которые нужно выполнить для удовлетворения запроса, с другой – независимая команда, т.е. реализация всего самостоятельно, без обращения за помощью к получателю. Последний вариант полезен, когда вы хотите определить команды, не зависящие от реализованных в системе классов, когда подходящего получателя не существует или когда получатель команде точно не известен.
- Поддержка отмены и повтора операций. Команды могут поддерживать отмену и повтор операций, если имеется возможность отменить результаты выполнения. Как правило, вся необходимая для этого информация сохраняется, в том числе:
- объект-получатель, который выполняет операции в ответ на запрос;
- аргументы операции, выполненной получателем;
- Исходные значения различных атрибутов получателя, которые могли измениться в результате обработки запроса.
Получатель должен предоставить операции, позволяющие команде вернуться в исходное состояние. Для поддержки всего одного уровня отмены приложению достаточно сохранять только последнюю выполненную команду. Если же нужны многоуровневые отмена и повтор операций, то придется вести список истории выполненных команд. Максимальная длина этого списка определяет число уровней отмены и повтора. Проход по списку в обратном направлении и откат результатов всех встретившихся по пути команд отменяет их действие. Проход в прямом направлении и выполнение встретившихся команд приводит к повтору выполнения действий.
- Накопление ошибок в процессе отмены. При выполнении, отмене и повторе команд иногда накапливаются ошибки, в результате чего состояние приложения оказывается отличным от первоначального. Поэтому порой необходимо сохранять в команде больше информации, дабы гарантировать, что объекты будут целиком восстановлены.
Известно большое количество задач, где подход к их решению задан с помощью шаблона "Команда":
- Любое приложение c возможностями отмены или повторения действий (undo/redo) пользователя.
- Сетевые распределенные системы, использующие запросы в виде объектов в качестве основного примитива инициализации каких-либо операций.
- Системы с поддержкой асинхронных вызовов, инкапсулирующие обратный вызов в виде опроса объекта.
Перечислять такие задачи можно бесконечно, важно понять, что шаблон "Команда"– один из самых распространенных шаблонов проектирования. Аналогией данного шаблона является полет на борту самолета. После того как самолет взлетел, есть только два возможных дальнейших пути–попасть в точку назначения или вернуться в точку вылета. Все остальные действия, которые проходят по ходу полета,не рассматриваются в отрыве от самого перелета и являются необходимыми составляющими для его завершения.