Лекция
Привет, мой друг, тебе интересно узнать все про структурные паттерны, тогда с вдохновением прочти до конца. Для того чтобы лучше понимать что такое структурные паттерны , настоятельно рекомендую прочитать все из категории Объектно-ориентированное программирование ООП.
В структурных паттернах рассматривается вопрос о том, как из классов и объектов образуются более крупные структуры. структурные паттерны уровня класса используют наследование для составления композиций из интерфейсов и реализаций.
Название
Adapter (адаптер), Wrapper (обертка)
Задача
Иногда класс из инструментальной библиотеки не удается использовать только потому, что его интерфейс не соответствует создаваемому приложению. В некоторых случаях мы можем поменять интерфейс на нужный, но если у нас нет исходного кода библиотеки такое решение не подходит. Еще один вариант - определить другой класс таким образом, что от будет адаптировать интерфейс одного класса к другому интерфейсу.
Паттерн Adapter преобразует интерфейс одного класса в интерфейс другого, который ожидают клиенты. Адаптер обеспечивает совместную работу классов с несовместимыми интерфейсами, которая без него была бы невозможна.
Результаты
Первый вариант паттерна называется адаптером объектов, второй адаптером классов. Результаты применения адаптеров объектов и классов немного различаются.
Адаптер классов
Адаптер объектов
Структура
Рисунок 2.8 Структура паттерна Adapter (адаптер объектов)
Рисунок 2.9 Структура паттерна Adapter (адаптер классов)
Название
Composite (компоновщик)
Мотивация
Такие приложения, как графические редакторы и редакторы электрических (схем, позволяют пользователям строить сложные диаграммы из более простых компонентов.
Рисунок 2.10 Структура паттерна Composite (пример)
Проектировщик может сгруппировать мелкие компоненты для формирования более крупных, которые, в свою очередь, могут стать основой для создания еще более крупных. В простой реализации допустимо было бы определить классы графических примитивов, например текста и линий, а также классы, выступающие в роли контейнеров для этих примитивов. Но у такого решения есть существенный недостаток. Программа, в которой эти классы используются, должна по-разному обращаться с примитивами и контейнерами, хотя пользователь чаще всего работает с ними единообразно. Необходимость различать эти объекты усложняет приложение. Паттерн компоновщик описывает, как можно применить рекурсивную композицию таким образом, что клиенту не придется проводить различие между простыми и составными объектами. Пример применения шаблона можно увидеть на на рис. 2.10.
Ключом к паттерну компоновщик является абстрактный класс, который представляет одновременно и примитивы, и контейнеры. В графической системе этот класс может называться Graphic. В нем объявлены операции, специфичные. Для каждого вида графического объекта (такие как Draw) и общие для всех составных объектов, например операции для доступа и управления потомками. Подклассы Line, Rectangle и Text (см. диаграмму выше) определяют примитивные графические объекты. В них операция Draw реализована соответственно для рисования прямых, прямоугольников и текста. Поскольку у примитивных объектов нет потомков, то ни один из этих подклассов не реализует операции, относящиеся к управлению потомками. Класс Picture определяет агрегат, состоящий из объектов Graphic. Реализованная в нем операция Draw вызывает одноименную функцию для каждого Потомка, а операции для работы с потомками уже не пусты. Поскольку интерфейс Класса Picture соответствует интерфейсу Graphic, то в состав объекта Picture Могут входить и другие такие же объекты. Ниже на диаграмме «оказана типичная структура составного объекта, рекурсивно скомпонованного из объектов класса Graphic.
Таким образом можно сформулировать задачу, для которой предназначен паттерн. Компоновщик компонует объекты в древовидные структуры для представления иерархии часть-целое. Позволяет клиентам единообразно трактовать индивидуальные и составные объекты.
Структура
Рисунок 2.11 Структура паттерна Composite
Результаты
Название
Decorator (декоратор)
Задачи
Иногда бывает нужно возложить дополнительные обязанности на отдельный объект, а не на класс в целом. Так, библиотека для построения графических интерфейсов пользователя должна «уметь» добавлять новое свойство, скажем, рамку или новое поведение (например, возможность прокрутки к любому элемент у интерфейса). Добавить новые обязанности допустимо с помощью наследования. При наследовании классу с рамкой вокруг каждого экземпляра подкласса будет рисоваться рамка. Об этом говорит сайт https://intellect.icu . Однако это решение статическое, а значит, недостаточно гибкое. Клиент не может управлять оформлением компонента рамкой. Более гибким является другой подход: поместить компонент в другой объект, называемый декоратором, который как раз и добавляет рамку. Декоратор следует интерфейсу декорируемого объекта, поэтому его присутствие прозрачно для клиентов компонента. Декоратор переадресует запросы внутреннему компоненту, но может выполнять и дополнительные действия (например, рисовать рамку) до или после переадресации. Поскольку декораторы прозрачны, они могут вкладываться друг в друга, добавляя тем самым любое число новых обязанностей.
Рисунок 2.12 Структура паттерна Decorator (пример)
Предположим, что имеется объект класса TextView, который отображает текст в окне. По умолчанию TextView не имеет полос прокрутки, поскольку они не всегда нужны. Но при необходимости их удастся добавить с помощью декоратора ScrollDecorator. Допустим, что еще мы хотим добавить жирную сплошную рамку вокруг объекта TextView. Здесь может помочь декоратор BorderDecorator. Ниже на диаграмме показано, как композиция объекта TextView с объектами BorderDecorator и ScrollDecorator порождает элемент для ввода текста, окруженный рамкой и снабженный полосой прокрутки.
Классы ScrollDecorator и BorderDecorator являются подклассами Decorator - абстрактного класса, который представляет визуальные компоненты, применяемые для оформления других визуальных компонентов. VisualComponent - это абстрактный класс для представления визуальных объектов. В нем определен интерфейс для рисования и обработки событий. Отметим, что класс Decorator просто переадресует запросы на рисование своему компоненту, а его подклассы могут расширять эту операцию (см. рис. 2.13).
Структура
Рисунок 2.13 Структура паттерна Decorator
Результаты
У паттерна декоратор есть, по крайней мере, два плюса и два минуса:
Название
Facade (фасад)
Назначение
Предоставляет унифицированный интерфейс вместо набора интерфейсов некоторой подсистемы. Фасад определяет интерфейс более высокого уровня, который упрощает использование подсистемы.
Разбиение на подсистемы облегчает проектирование сложной системы в целом. Общая цель всякого проектирования - свести к минимуму зависимость подсистем друг от друга и обмен информацией между ними. Один из способов решения этой задачи - введение объекта фасад, предоставляющий единый упрощенный интерфейс к более сложным системным средствам.
Структура
Рисунок 2.14 Структура паттерна Facade
Участники
Facade – фасад. Знает, каким классам подсистемы адресовать запрос; делегирует запросы клиентов подходящим объектам внутри подсистемы;
Классы подсистемы. Реализуют функциональность подсистемы; выполняют работу, порученную объектом Facade; ничего не "знают" о существовании фасада, то есть не хранят ссылок на него.
Отношения
Клиенты общаются с подсистемой, посылая запросы фасаду. Он переадресует их подходящим объектам внутри подсистемы. Хотя основную работу выполняют именно объекты подсистемы, фасаду, возможно, придется преобразовать свой Интерфейс в интерфейсы подсистемы. Клиенты, пользующиеся фасадом, не имеют прямого доступа к объектам подсистемы.
Результаты
У паттерна фасад есть следующие преимущества:
Название
Low Coupling (Низкая связность)
Проблема
Степень связанности (coupling) — это мера, определяющая насколько жестко один элемент связан с другими элементами, либо каким количеством данных о других элементах он обладает. Элемент с низкой степенью связанности (или слабым связыванием) зависит от не очень большого числа других элементов. Выражение "очень много" зависит от контекста, однако необходимо провести его оценку.
Класс с высокой степенью связанности (или жестко связанный) зависит от множества других классов. Однако наличие таких классов нежелательно, поскольку оно приводит к возникновению следующих проблем.
Решение
Распределить обязанности таким образом, чтобы степень связанности оставалась низкой.
Результаты
Пример
Пусть в системе имеются классы Payment, Sale и Register. Предположим, что необходимо создать экземпляр класса Payment и связать его с объектом Sale. Какой класс должен отвечать за выполнение этой операции? Поскольку в реальной предметной области регистрация объекта Payment выполняется объектом Register, в соответствии с шаблоном Creator, объект Register является хорошим кандидатом для создания объекта Payment. Затем экземпляр объекта Register должен передать сообщение addPayment объекту Sale, указав в качестве параметра новый объект Payment. Приведенные рассуждения отражены на фрагменте диаграммы взаимодействий, представленной на рис. 2.15.Такое распределение обязанностей предполагает, ЧТО класс Register обладает знаниями о данных класса Payment (т.е. связывается с ним).
Обратите внимание на обозначения, принятые в языке UML. Экземпляру объекта Payment присвоено явное имя р, чтобы его можно было использовать в качестве параметра сообщения 2.
Рисунок 2.15 Диаграмма взаимодействия. Сильная связность
Альтернативный способ создания объекта Payment и его связывания с объектом Sale состоит в том, что бы возложит обязанность за создание объекта Payment на Sale (рис. 2.16)
Рисунок 2.16 Диаграмма взаимодействия. Слабая связность
Когда не следует применять шаблон
Высокая степень связывания с устойчивыми элементами не представляет проблемы. Например, приложение J2EE можно жестко связать с библиотеками Java (java.util и т.п.), поскольку эти библиотеки широко распространены и стабильны.
Выбери свою игру
Высокая степень связывания сама по себе не является проблемой. Проблемой является жесткое связывание с неустойчивыми в некотором отношении элементами.
Важно понимать следующее. Разработчик может обеспечивать гибкость программы, реализовывать принцип инкапсуляции и придерживаться принципа слабого связывания во многих аспектах системы. Однако без убедительной мотивации не следует во что бы то ни стало бороться за уменьшение степени связывания объектов. Разработчики должны выбрать "свою игру", чтобы снизить степень связывания и обеспечить инкапсуляцию. При этом особое внимание нужно уделить неустойчивым или быстро изменяющимся элементам.
Название
Information Expert (информационный эксперт)
Проблема
Каков наиболее общий принцип распределения обязанностей между объектами при объектно-ориентированном проектировании?
В модели системы могут быть определены десятки или сотни программных классов, а в приложении может потребоваться выполнение сотен или тысяч обязанностей. Во время объектно-ориентированного проектирования при формулировке принципов взаимодействия объектов необходимо распределить обязанности между классами.
При правильном выполнении этой задачи система становится гораздо проще для понимания, поддержки и расширения. Кроме того, появляется возможность повторного использования уже разработанных компонентов в последующих приложениях.
Решение
Назначить обязанность информационному эксперту — классу, у которого имеется информация, требуемая для выполнения обязанности.
Результаты
Соответствующее поведение системы обеспечивается несколькими классами содержащими требуемую информацию. Это приводит к определениям классов, которые гораздо проще понимать и поддерживать. Кроме того, поддерживается шаблон High Cohesion
Пример
В приложении некоторому классу необходимо знать общую сумму продажи. Начинайте распределение обязанностей с их четкой формулировки. С этой точки зрения можно сформулировать следующее утверждение.
Какой класс должен отвечать за знание общей суммы продажи?
Согласно шаблону Information Expert, нужно определить, объекты каких классов содержат информацию, необходимую для вычисления общей суммы.
Теперь возникает ключевой вопрос: на основе какой модели нужно анализировать информацию - модели предметной области или проектирования? Модель предметной области иллюстрирует концептуальные классы из предметной области системы, а в модели проектирования показаны программные классы.
Ответ на этот вопрос сводится к следующему:
Если в модели проектирования имеются соответствующие классы, в первую очередь, следует использовать ее.
В противном случае нужно обратиться к модели предметной области и постараться уточнить ее для облегчения создания соответствующих программных классов.
Например, предположим, мы находимся в самом начале этапа проектирования, когда модель проектирования представлена в минимальном объеме. Следовательно, кандидатуру на роль информационного эксперта следует искать в модели предметной области. Вероятно, на эту роль подойдет концептуальный класс Sale. Тогда в модель проектирования нужно добавить соответствующий программный класс под именем Sale и присвоить ему обязанность вычисления общей стоимости, реализуемую с помощью вызова метода getTotal. При таком подходе сокращается разрыв между организацией программных объектов и соответствующих им понятий из предметной области.
Чтобы рассмотреть этот пример подробнее, обратимся к фрагменту модели предметной области, представленному на рис. 2.17.
Рисунок 2.17 Фрагмент модели предметной области
Какая информация требуется для вычисления общей суммы? Необходимо узнать стоимость всех проданных товаров SalesLineltem и просуммировать эти промежуточные суммы. Такой информацией обладает лишь экземпляр объекта Sale. Следовательно, с точки зрения шаблона Information Expert объект Sale v подходит для выполнения этой обязанности, т.е. является информационным экспертом (information expert).
Как уже упоминалось, подобные вопросы распределения обязанностей зачастую возникают при создании диаграмм взаимодействий. Представьте, что вы приступили к работе, начав создание диаграмм для распределения обязанностей между объектами. Принятые решения иллюстрируются на фрагменте диаграммы Взаимодействий, представленном на рис. 2.18.
Однако на данном этапе выполнена не вся работа. Какая информация требуется для вычисления промежуточной суммы элементов продажи? Необходимы значения атрибутов SalesLineltem.quantity и SalesLineltem.price. Объекту SalesLineltem известно количество товара и известен связанный с ним объект ProductSpecif ication. Следовательно, в соответствии с шаблоном Expert, промежуточную сумму должен вычислять объект SalesLineltem. Другими словами, этот объект является информационным экспертом.
Рисунок 2.18 Распределение обязанностей в соответствии с паттерном Information Expert
В терминах диаграмм взаимодействий это означает, что объект Sale должен передать сообщения getSubtotal каждому объекту SalesLineltem, а затем просуммировать полученные результаты. Этот процесс проиллюстрирован на рис. 2.19
Рисунок 2.19 Распределение обязанностей в соответствии с паттерном Information Expert
Для выполнения обязанности, связанной со знанием и предоставлением промежуточной суммы, объекту SalesLineltem должна быть известна стоимость товара. В данном случае в качестве информационного эксперта будет выступать объект ProductSpecification (рис. 2.20).
В завершение можно сказать следующее. Для выполнения обязанности "знать и предоставлять общую сумму продажи трем объектам классов" были следующим образом присвоены три обязанности:
Рассмотрение и распределение обязанностей выполнялись в процессе создания диаграммы взаимодействий. Затем полученные результаты могут быть реализованы в разделе методов диаграммы классов. При назначении обязанностей, согласно шаблону Expert, был применен следующий принцип: обязанности связываются с тем объектом, который имеет информацию, необходимую для их выполнения.
Рисунок 2.20 Распределение обязанностей в соответствии с паттерном Information Expert
Я хотел бы услышать твое мнение про структурные паттерны Надеюсь, что теперь ты понял что такое структурные паттерны и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Объектно-ориентированное программирование ООП
Ответы на вопросы для самопроверки пишите в комментариях, мы проверим, или же задавайте свой вопрос по данной теме.
Комментарии
Оставить комментарий
Объектно-ориентированное программирование ООП
Термины: Объектно-ориентированное программирование ООП