Аналитические шаблоны проектирования приложений

Структурные шаблоны проектирования

Показывать лекцию целиком

Введение

Согласно классификации, предлагаемой по ходу данного курса, описание информационной системы в терминах классов или объектов следует считать наиболее "низким", фундаментальным уровнем ее представления.

При этом при моделировании системы на данном уровне традиционно описывают структуру не только с позиции ее строения, но и с точки зрения инфраструктурного обеспечения. В этой главе мы изучим только структурные шаблоны, а к "поведенческим" подступимся в следующей.

Подробно рассмотрим 10 наиболее значимых шаблонов, изучение которых сформирует необходимый базис и позволит постепенно кристаллизовать информацию о паттернах проектирования у читателей, запутанных архитектурными и интеграционными шаблонами.

Необходимо очень внимательно отнестись к освоению данной группы паттернов. От того, насколько понятными и используемыми они для вас станут, зависит успешность применения более сложных паттернов в вашей проектной и операционной деятельности.

Шаблоны структуры объектов

В качестве структурных паттернов проектирования сегодня мы рассмотрим следующие:

"Адаптер", "Декоратор", "Заместитель", "Информационный эксперт", "Компоновщик", "Мост", "Низкая связанность", "Приспособленец", "Устойчивый к изменениям", "Фасад".

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

Адаптер

Структурный шаблон "Адаптер" востребован в ситуации, когда необходимо организовать использование функций определенного бизнес-объекта, недоступного для модификации. Это происходит, когда система или ее компонент имеют требуемые данные и функциональность, но не имеют подходящего интерфейса.

Если сформулировать его назначение иначе, то шаблон "Адаптер" обеспечивает взаимодействие несовместимых интерфейсов путем предоставления единого устойчивого интерфейса для нескольких компонентов.

Алгоритм реализации шаблона "Адаптер" основан на конвертировании от исходного интерфейса конкретного компонента к другому виду с помощью промежуточного объекта –непосредственно самого "Адаптера".То есть разрабатывается специальный объект с общим интерфейсом, который перенаправляет связи от внешних объектов к самому"Адаптеру". Этот структурный шаблон является своеобразным воплощением принципа объектно-ориентированного программирования – полиморфизма. Система содержит объекты разных типов, но использующие их объекты взаимодействуют одним и тем же образом, используя "Адаптер" для преобразования различных данных к единому представлению.

Чаще всего "Адаптер" применяется, если необходимо создать определенный класс, производный от уже существующего.

Другими словами, данный шаблон предполагает создание нового интерфейса для требуемого объекта, который мы не можем использовать из-за его неподходящего интерфейса.

Это шаблон общего характера и очень часто используется в комбинации с другими шаблонами проектирования.

"Банда четырех" (основная группа евангелисты объектно-ориентированного подхода к разработке программного обеспечения) следующим образом определяет назначение паттерна"Адаптер":

"Преобразование исходного интерфейса класса в интерфейс, более подходящий для конкретных нужд. Применение этого шаблона позволяет организовать совместную работу несовместимых интерфейсов".

Шаблон "Адаптер" позволяет включать уже существующие объекты в новые структуры, независимо от различий в их интерфейсах. Интерфейс включающего объекта и его класс приводится в соответствие с новыми требованиями, а вызовы методов преобразуются в вызовы методов включенного класса.

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

На сегодняшний момент распространены два типа этого шаблона:

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

    Шаблон "Адаптер" можно сравнить с электрическим переходником, необходимым для подключения "евро" -вилок с цилиндрическими наконечниками к азиатским розеткам.

    (рис 5.2.1) Шаблон "Адаптер"

    Декоратор

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

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

    Возможность динамически добавлять новые обязанности, не прибегая к порождению подклассов, облегчает понимание основных обязанностей объектов и не запутывает основную бизнес-логику информационных систем:

  • Системный компонент определяет интерфейс объекта, на который могут быть динамически возложены дополнительные обязанности.
  • Конкретный компонент определяет объект, на который возлагаются дополнительные обязанности.
  • Шаблон "Декоратор" хранит ссылку на объект (компонент), определяет его интерфейс и переадресует на него рабочие запросы.
  • "Декоратор" позволяет реализовать большую гибкость, чем у статического наследования, создаваемого путем разработки подклассов.

    Можно добавлять и удалять обязанности во время выполнения программы, в то время как при использовании наследования надо было бы создавать новый класс для каждой дополнительной обязанности.

    Данный шаблон позволяет не создавать классов, перегруженных методами. Новые обязанности можно добавлять только по мере необходимости, не перегружая программное обеспечение лишними классами.

    Заместитель

    В случаях, когда необходимо управлять доступом к объекту, так чтобы создавать громоздкие компоненты только "по требованию", оптимально использовать шаблон "Заместитель".

    Суть "Заместителя" состоит в том, что он хранит ссылку, которая позволяет ему обратиться к реальному субъекту только при необходимости, задействуя системные ресурсы.

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

    Шаблон "Заместитель" может иметь и другие обязанности:

  • Удаленный "Заместитель" может отвечать за кодирование запроса и его аргументов и отправку закодированного запроса.
  • Виртуальный "Заместитель" может кэшировать дополнительную информацию о реальном компоненте.
  • Защищающий "Заместитель" может проверять, имеет ли вызывающий объект необходимые для выполнения запроса права.
  • Шаблон "Заместитель" достаточно часто используют в паре с шаблоном "Адаптер".

    Информационный эксперт

    Когда в системе должна аккумулироваться, преобразовываться и удаляться необходимая информация, применяется шаблон "Информационный эксперт".

    Смысл реализации этого паттерна состоит в том, что обязанность за аккумуляцию и преобразование информации назначается определенному системному классу, который должен обладать необходимой информацией и функциональностью.

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

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

    Компоновщик

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

  • Окно пользовательского интерфейса – контейнер для более простых элементов – панелей, кнопок, полей ввода и т.д.
  • Панель тоже является контейнерными объектами вплоть до конечных элементов.
  • В подобных программных продуктах часто возникают ситуации, когда необходимо обработать группу, структуру или структуры объектов одновременно. Для этого целесообразно определить классы для комплексных и атомарных объектов таким образом, чтобы они реализовывали один и тот же программный интерфейс, но система должна уметь различать составные и атомарные объекты соответственно.

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

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

    Достоинствами шаблона "Компоновщик" являются:

  • легкость добавления новых примитивных или составных объектов;
  • простота структуры программы:
  • примитивные и составные объекты обрабатываются одинаковым образом.
  • К недостаткам следует отнести неудобство реализации запрета на добавление в составной объект компонентов определенных типов.

    Мост

    Приступая к освоению очередного структурного шаблона, немного порассуждаем о принципах анализа, которые наглядно демонстрируют необходимость применения шаблонов проектирования в целом и шаблона "Мост" в частности.

    Анализы общности и изменчивости включают рекомендации по поиску того, что изменяется и что является общим в заданной конкретной ситуации. Решение любой задачи состоит в том, чтобы определить, где возможны изменения (анализ общности), а затем установить, как это изменение может происходит (анализ изменчивости).

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

    Общие концепции представляются в системе в виде абстрактных классов.

    Вариации, обнаруженные при анализе изменчивости, реализуются посредством создания более конкретных классов–производных от этих абстрактных классов.

    Таким образом, если требуется на уровне приложения разделять абстракцию и реализацию так, чтобы и то и другое можно было изменять независимо, применяют паттерн "Мост".

    Существующие подходы к решению подобных задач основаны на принципе наследования, что приводит к тому, что реализация жестко привязывается к абстракции и последующая независимая модификация очень сложна.

    "Мост"– один из самых трудных для понимания шаблонов проектирования. Это вызвано тем, что он является очень мощным и часто применяется в самых различных ситуациях. Кроме того, он противоречит распространенной практике применения механизма наследования для реализации специальных случаев.

    Алгоритм его реализации заключается в помещении абстракции и реализации в отдельные иерархии классов. Этот подход имеет неоспоримое достоинство при выполнении только реализации во время исполнения программы:

  • Абстрактный класс определяет свой интерфейс и хранит ссылку на реализуемый объект.
  • Реализуемый объект определяет интерфейс для классов реализации:
  • Он не обязан точно соответствовать интерфейсу абстрактного класса.
  • Оба интерфейса могут быть совершенно различны.
  • Реализуемый класс предоставляет только конкретные операции, а абстрактный класс определяет операции более высокого уровня, базирующиеся на этих примитивных.
  • При обращении к абстрактному классу он перенаправляет все запросы реализуемому классу.
  • Применение шаблона "Мост" позволяет получить более устойчивые проектные решения для представления элементов абстракции и реализации, упрощая их возможное последующее изменение.

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

    В общем случае, чтобы определить, какой именно структурный шаблон лучше использовать, следует сопоставить его с характеристиками и поведением сущностей в проблемной области.

    Низкая связанность

    Шаблон "Низкая связанность" воплощает принцип проектирования, который позволяет распределить обязанности между объектами таким образом, чтобы степень связанности между системами оставалась низкой.

    Степень связанности – мера, определяющая, насколько жестко один элемент связан с другими элементами, либо каким количеством данных о других элементах он обладает.

    Программный компонент с низкой степенью связанности зависит от не очень большого числа других системных элементов и имеет следующие свойства:

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

    Шаблон "Низкая связанность" позволяет избежать следующих проблем:

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

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

    "Приспособленец"– это экземпляр объекта, который выдает себя за группу самостоятельных экземпляров.

    Смысл использования шаблона "Приспособленец" состоит в моделировании сущности, число которых велико. Имеет смысл использовать данный паттерн, если одновременно выполняются следующие условия:

  • В приложении используется большое число объектов, что приводит к высоким системным затратам.
  • Большую часть состояния объектов можно вынести вовне оперативного использования:
  • Часть состояния объекта является контекстной и может быть легко вынесена во внешние структуры.
  • После вынесения части состояния все экземпляры становятся одинаковыми и это дает возможность заменить их одним.
  • Приложение не проверяет идентичность объектов, т.к. в этом случае все якобы самостоятельные экземпляры являются одним объектом.
  • Реализация шаблона выполняется за счет создания разделяемого объекта, который можно использовать одновременно в нескольких контекстах. В каждом контексте он должен выглядеть как независимый объект, то есть быть неотличимым от исходного экземпляра. Паттерн "Приспособленец" декларирует интерфейс, с помощью которого конкретные приспособленцы могут получить внешнее состояние или как-то воздействовать на него. Кроме прочего, экземпляр приспособленца хранит внутреннее состояние, в то время как внешнее состояние хранится или вычисляется использующими его процессами обработки.

    Наличие различных вариантов внутреннего состояния приводит к необходимости создания набора приспособленцев. При запросе нового экземпляра выполняется поиск уже реализованного варианта. Если такого не находится, то порождается новый.

    Таким образом, можно отметить следующих "участников" шаблона:

  • "Приспособленец".
  • "Пул или Фабрика приспособленцев" – объект, создающий и управляющий экземплярами приспособленцев.
  • "Неразделяемый объект" – экземпляр, существующий вне рамок шаблона.
  • Главным моментом при реализации шаблона является правильное разделение состояния объекта на внутреннее и внешнее. Именно возможность сделать это корректно и правильно является главным фактором применимости шаблона.

    Очень важно, чтобы вынесенная информация вписывалась в абстракцию ее нового хранилища.

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

  • Вынесенное состояние не сохраняется отдельно.
  • Рассчитывается непосредственно перед вызовом (шаблон "Компоновщик").
  • Шаблон "Приспособленец" часто используется совместно с шаблоном "Компоновщик" для уменьшения числа экземпляров частей составных объектов. Необходимо осознавать минус такого подхода, заключающегося в отсутствии части данных, которые могут быть востребованы в дальнейшем при организации бизнес-логики разрабатываемого приложения.

    При разработке набора приспособленцев стоит продумать механизм определения и освобождения неиспользуемых экземпляров.

    Результатами использования шаблона "Приспособленец" являются:

  • уменьшение количества экземпляров объектов, оперируемых в информационной системе;
  • оптимизация процесса управления этими объектами.
  • Устойчивый к изменениям

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

    Задача модификации системы наиболее актуальна в условиях динамически изменяющихся требований. Зачастую удается выделить "места неустойчивости" системы, которые наиболее часто будут подвержены изменению. Тогда сущность шаблона "Устойчивый к изменениям" заключается в устранении подобных точек путем определения их в качестве интерфейсов и реализации для них различных функциональных вариантов. Основная цель состоит в распределении обязанностей продукта таким образом, чтобы обеспечить устойчивый интерфейс.

    Термин "интерфейс" используется для обозначения способа обеспечения доступа.

    Шаблон"Устойчивый к изменениям"описывает ключевой принцип, на основе которого реализуются механизмы,обеспечивающие гибкость и защиту системы от влияния изменений внешних систем.

    Подобными механизмами являются:

  • инкапсуляция данных, универсальные интерфейсы;
  • проектирование на основе данных и пр.
  • Определившись со способом реализации, необходимо помнить о том, когда не следует применять данный паттерн.

    Говоря об устойчивости программного обеспечения, следует выделить два типа точек:

  • Точки вариации:
  • Места"ветвления" в существующей на данный момент системе или в требованиях к ней– например, необходимость поддержки нескольких интерфейсов для системы вычисления налоговых платежей.
  • Точки эволюции:
  • Предполагаемые точки ветвления, которые могут возникнуть в будущем, однако не определяемые существующими требованиями.
  • Сосредоточиться стоит именно на первых типах точек, инкапсулируя и повышая устойчивость функционала, востребованного в конкретный момент. Точки эволюций необходимо держать под контролем, но трата чрезмерного количества усилий на создание обобщенного и модифицируемого кода является расточительством.

    В результате оптимального использования принципов шаблона "Устойчивый к изменениям" можно получить следующие преимущества:

  • Легкость добавления новых расширений и вариаций.
  • Возможность добавления новых реализаций.
  • Слабое связывание.
  • Минимизация влияния изменений.
  • Фасад

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

    Назначение паттерна "Фасад"– упростить работу с существующей системой, определив собственный интерфейс обращения к ней. При этом определяемый интерфейс должен использовать только определенное множество функций системы или организовать взаимодействие с ней неким специфическим образом.

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

    Реализация других компонентов подсистемы закрыта и не видна внешним компонентам. Фасадный объект обеспечивает реализацию паттерна "Устойчивый к изменениям".

    В основе реализации шаблона лежит определение нового класса (или классов) с требуемым интерфейсом, которые должны опираться на функциональные возможности существующей системы.

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

    Этот шаблон также может использоваться для сокрытия или инкапсуляции базовой системы. В этом случае профильные классы включают основную систему как свой закрытый компонент. Поэтому основная система будет взаимодействовать только с "Фасадом", оставаясь недоступной и невидимой для всех остальных пользователей.

    Подобная реализация может быть использована для обеспечения следующих требований:

  • Контроль за использованием системы:
  • Вынуждая пользователей все обращения к системе выполнять только через фасад, легко можно контролировать их доступ к ней.
  • Упрощение замены системы:
  • В будущем может потребоваться заменить существующую систему новой. Для этих целей "Фасад" может использоваться как место агрегации наиболее полезных методов заменяемой системы с последующим переносом в новую.
  • Представление базовой системы как закрытого компонента.
  • В основном шаблон "Фасад" используется в тех случаях, когда необходим новый способ взаимодействия с системой – более простой в сравнении с уже существующим.

    "Фасад" упрощает использование сложной системы, отдельной части системы или обеспечивает обращение к системе некоторым специфическим образом. В результате применения шаблона "Фасад" мы получим новую, более простую в использовании систему, которая будет точно соответствовать нашим потребностям.

    Применение шаблона "Фасад" наиболее востребовано в следующих условиях:

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

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

    Структурные шаблоны проектирования решают задачу компоновки системы на основе классов и объектов. При этом могут использоваться следующие механизмы:

  • Наследование. Базовый класс определяет интерфейс, а подклассы –его реализацию.
  • Структуры на основе наследования получаются статичными.
  • Композиция.
  • Структуры строятся путем объединения объектов некоторых классов.
  • Композиция позволяет получать структуры, которые можно изменять во время выполнения, и пр.
  • Структурные паттерны описывают создание сложных и комплексных объектов, оптимизируют взаимодействие между разными системными компонентами.

    Выводы

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

    Используя структурные шаблоны в практической деятельности при разработке программного обеспечения, можно добиться высоких результатов в создании качественных информационных систем.

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

    В следующей главе мы приступим к изучению не менее интересных поведенческих паттернов.

    Вернуться к учебному плану