Структурные шаблоны проектирования
Введение
Согласно классификации, предлагаемой по ходу данного курса, описание информационной системы в терминах классов или объектов следует считать наиболее "низким", фундаментальным уровнем ее представления.
При этом при моделировании системы на данном уровне традиционно описывают структуру не только с позиции ее строения, но и с точки зрения инфраструктурного обеспечения. В этой главе мы изучим только структурные шаблоны, а к "поведенческим" подступимся в следующей.
Подробно рассмотрим 10 наиболее значимых шаблонов, изучение которых сформирует необходимый базис и позволит постепенно кристаллизовать информацию о паттернах проектирования у читателей, запутанных архитектурными и интеграционными шаблонами.
Необходимо очень внимательно отнестись к освоению данной группы паттернов. От того, насколько понятными и используемыми они для вас станут, зависит успешность применения более сложных паттернов в вашей проектной и операционной деятельности.
Шаблоны структуры объектов
В качестве структурных паттернов проектирования сегодня мы рассмотрим следующие:
"Адаптер", "Декоратор", "Заместитель", "Информационный эксперт", "Компоновщик", "Мост", "Низкая связанность", "Приспособленец", "Устойчивый к изменениям", "Фасад".
Некоторые структурные шаблоны часто используются совместно, решая специализированные практические задачи, но каждую задачу можно решить, используя различные паттерны проектирования. Разница в выборе того или иного паттерна должна быть обоснована с точки зрения выбора конкретных условий использования приложения, разработанного с применением обсуждаемых шаблонов. Шаблон должен снимать проблемы, возникшие здесь и сейчас. При этом дальновидность разработчика заключается в том, насколько просто предложенное шаблонное решение сможет поддержать будущие программные изменения и преобразования.
Адаптер
Структурный шаблон "Адаптер" востребован в ситуации, когда необходимо организовать использование функций определенного бизнес-объекта, недоступного для модификации. Это происходит, когда система или ее компонент имеют требуемые данные и функциональность, но не имеют подходящего интерфейса.
Если сформулировать его назначение иначе, то шаблон "Адаптер" обеспечивает взаимодействие несовместимых интерфейсов путем предоставления единого устойчивого интерфейса для нескольких компонентов.
Алгоритм реализации шаблона "Адаптер" основан на конвертировании от исходного интерфейса конкретного компонента к другому виду с помощью промежуточного объекта –непосредственно самого "Адаптера".То есть разрабатывается специальный объект с общим интерфейсом, который перенаправляет связи от внешних объектов к самому"Адаптеру". Этот структурный шаблон является своеобразным воплощением принципа объектно-ориентированного программирования – полиморфизма. Система содержит объекты разных типов, но использующие их объекты взаимодействуют одним и тем же образом, используя "Адаптер" для преобразования различных данных к единому представлению.
Чаще всего "Адаптер" применяется, если необходимо создать определенный класс, производный от уже существующего.
Другими словами, данный шаблон предполагает создание нового интерфейса для требуемого объекта, который мы не можем использовать из-за его неподходящего интерфейса.
Это шаблон общего характера и очень часто используется в комбинации с другими шаблонами проектирования.
"Банда четырех" (основная группа евангелисты объектно-ориентированного подхода к разработке программного обеспечения) следующим образом определяет назначение паттерна"Адаптер":
"Преобразование исходного интерфейса класса в интерфейс, более подходящий для конкретных нужд. Применение этого шаблона позволяет организовать совместную работу несовместимых интерфейсов".
Шаблон "Адаптер" позволяет включать уже существующие объекты в новые структуры, независимо от различий в их интерфейсах. Интерфейс включающего объекта и его класс приводится в соответствие с новыми требованиями, а вызовы методов преобразуются в вызовы методов включенного класса.
Применение шаблона "Адаптер" во время разработки программного продукта позволяет не принимать во внимание возможные различия в интерфейсах уже существующих объектов. Если имеется класс, обладающий требуемыми методами и свойствами, то при необходимости всегда можно будет воспользоваться рассматриваемым шаблоном для приведения его интерфейсов к нужному виду. Эта особенно важно, если применяется сразу несколько шаблонов, поскольку многие шаблоны требуют, чтобы используемые в них классы были порождены от одного и того же класса.
На сегодняшний момент распространены два типа этого шаблона:
- Объектный. Вариант шаблона, реализуемый посредством помещения, адаптируемого в другой, адаптирующий объект.
- Классовый. Использует механизм множественного наследования и получил название классового.
Выбор варианта определяется спецификой проблемной области.
Шаблон "Адаптер" можно сравнить с электрическим переходником, необходимым для подключения "евро" -вилок с цилиндрическими наконечниками к азиатским розеткам.
Декоратор
Если требуется возложить дополнительные обязанности на отдельный объект, а не на класс, генерирующий объекты, целесообразно применять шаблон "Декоратор".
Его множественное применение к одному системному компоненту позволяет произвольным образом сочетать обязанности, например, одно свойство можно добавить дважды.
Возможность динамически добавлять новые обязанности, не прибегая к порождению подклассов, облегчает понимание основных обязанностей объектов и не запутывает основную бизнес-логику информационных систем:
- Системный компонент определяет интерфейс объекта, на который могут быть динамически возложены дополнительные обязанности.
- Конкретный компонент определяет объект, на который возлагаются дополнительные обязанности.
- Шаблон "Декоратор" хранит ссылку на объект (компонент), определяет его интерфейс и переадресует на него рабочие запросы.
"Декоратор" позволяет реализовать большую гибкость, чем у статического наследования, создаваемого путем разработки подклассов.
Можно добавлять и удалять обязанности во время выполнения программы, в то время как при использовании наследования надо было бы создавать новый класс для каждой дополнительной обязанности.
Данный шаблон позволяет не создавать классов, перегруженных методами. Новые обязанности можно добавлять только по мере необходимости, не перегружая программное обеспечение лишними классами.
Заместитель
В случаях, когда необходимо управлять доступом к объекту, так чтобы создавать громоздкие компоненты только "по требованию", оптимально использовать шаблон "Заместитель".
Суть "Заместителя" состоит в том, что он хранит ссылку, которая позволяет ему обратиться к реальному субъекту только при необходимости, задействуя системные ресурсы.
Объекты, создаваемые классами, реализованными на основе шаблона "Заместитель", могут обращаться к необходимому системному компоненту, если их интерфейсы идентичны, поскольку интерфейс компонента должен быть идентичен интерфейсу заместителя так, что они могут быть взаимозаменяемы и заместитель может контролировать создание, изменение, удаление компонента или его экземпляра.
Шаблон "Заместитель" может иметь и другие обязанности:
- Удаленный "Заместитель" может отвечать за кодирование запроса и его аргументов и отправку закодированного запроса.
- Виртуальный "Заместитель" может кэшировать дополнительную информацию о реальном компоненте.
- Защищающий "Заместитель" может проверять, имеет ли вызывающий объект необходимые для выполнения запроса права.
Шаблон "Заместитель" достаточно часто используют в паре с шаблоном "Адаптер".
Информационный эксперт
Когда в системе должна аккумулироваться, преобразовываться и удаляться необходимая информация, применяется шаблон "Информационный эксперт".
Смысл реализации этого паттерна состоит в том, что обязанность за аккумуляцию и преобразование информации назначается определенному системному классу, который должен обладать необходимой информацией и функциональностью.
Роль информационного эксперта может играть не только один, но и несколько специализированных классов, каждый из которых должен отвечать за свои функциональные возможности. Обязанности распределяются в соответствии с предоставляемой по результатам выполнения информацией. Таким образом поддерживается инкапсуляция, то есть объекты используют свои собственные данные для выполнения поставленных задач.
Если объект, обладающий наиболее полной информацией, будет отвечать и за сохранение этой информации в базе данных, то получится, что логика приложения и логика связи с базой данных "помещаются" в один класс, что нарушает принцип разделения обязанностей основных объектов системы, и, кроме того, логика связи с базой данных будет дублироваться во многих других классах.