🏠 Маст-хэв для каждого программиста: структурные шаблоны проектирования. Зачем нужны и когда их стоит использовать
В этой части мы рассмотрим применение основных структурных шаблонов – Адаптера, Моста, Компоновщика, Декоратора, Фасада, Легковеса и Заместителя.
Структурный шаблон «Адаптер»
Назначение
Позволяет классам с различными интерфейсами работать вместе, создавая общий объект, с помощью которого они могут общаться и взаимодействовать.
Когда использовать
- Если используемый класс не соответствует требованиям интерфейса.
- Поведение объекта с его состоянием связывают сложные условия.
- Переходы между состояниями должны быть явными.
Структура
Клиент (Client) – класс, содержащий существующую бизнес-логику программы. Клиент обращается к Адаптеру (Adapter), который перенаправляет обращение к Adaptee через конкретный адаптер (Concrete Adapter).
Адаптируемый класс (Adaptee) – класс, интерфейс которого необходимо привести к нужному виду. Адаптер — это класс, который может одновременно работать и с клиентом, и с сервисом. Он реализует клиентский интерфейс и содержит ссылку на объект сервиса.
Пример
Приложение получает биржевые котировки, используя формат XML. Если разработчику потребуется подключить дополнительный сервис, использующий JSON, возникнет проблема. Решить проблему сможет адаптер, который будет конвертировать данные из JSON в XML.
Структурный шаблон «Мост»
Назначение
Разделяет один или несколько классов на две отдельные иерархии — абстракцию и реализацию, позволяя изменять их независимо друг от друга.
Когда использовать
- Если абстракции и реализации не должны быть связаны во время компиляции.
- Абстракции и реализации должны быть независимо расширяемыми.
- Изменения в реализации абстракции не должны влиять на клиентов.
- Детали реализации должны быть скрыты от клиента.
Абстракция (Abstraction) содержит управляющую логику. Абстракция делегирует задания связанному объекту реализации. Реализатор (Implementor) задает общий интерфейс для всех реализаций. Все методы, которые здесь описаны, будут доступны из класса абстракции и его подклассов. Конкретные реализаторы (Concrete Implementer) содержат платформо-зависимый код.
Пример
Виртуальная машина Java (JVM) имеет свой собственный набор функций, которые абстрагируют использование графического интерфейса, системного журнала и выполнение байт-кода, но фактическая реализация этих функций передается операционной системе, на которой работает JVM. Когда приложение поручает JVM отобразить окно, оно передает вызов рендеринга конкретной реализации JVM, которая знает, как взаимодействовать с операционной системой, чтобы отобразить окно.
Структурный шаблон «Компоновщик»
Назначение
Облегчает создание иерархий объектов, где каждый объект может рассматриваться независимо или как набор вложенных объектов через один и тот же интерфейс.
Когда использовать
- Необходимы иерархические представления объектов.
- Объекты и композиции объектов должны рассматриваться в одинаковом порядке.
Компонент (Component) определяет общий интерфейс для простых и составных компонентов дерева. Лист (Leaf) – простой компонент дерева, не имеющий ответвлений. Классы листьев содержат большую часть рабочего кода.
Пример
Иногда информация, отображаемая в корзине, является описанием одного товара, в то время как в других случаях она представляет собой совокупность описаний нескольких товаров. Реализуя элементы с помощью «Компоновщика», можно одинаково обращаться с агрегатами и элементами, что позволяет нам просто перебирать дерево и вызывать функциональность для каждого элемента. Вызвав метод getCost() на любом узле, мы получим стоимость этого элемента плюс стоимость всех дочерних элементов, что позволяет обрабатывать элементы одинаково, будь то отдельные элементы или группы элементов.
Структурный шаблон «Декоратор»
Назначение
Позволяет динамически обертывать объекты, чтобы изменить их существующие обязанности и поведение.
Когда использовать
- Обязанности и поведение объектов должны изменяться динамически.
- Конкретные реализации должны быть отделены от обязанностей и поведения.
- Использование подклассов для модификации непрактично или невозможно.
- Конкретная функциональность не должна располагаться высоко в иерархии объектов.
- Допустимо присутствие множества мелких объектов, окружающих конкретную реализацию.
Структура
Компонент (Component) определяет общий интерфейс оберток и оборачиваемых объектов. Конкретный компонент (Concrete Component) определяет класс оборачиваемых объектов. Декоратор (Decorator) хранит ссылку на вложенный объект-компонент и делегирует все свои операции вложенному объекту. Дополнительное поведение определено в конкретных декораторах (Concrete Decorator).
Пример
Многие компании настраивают свои почтовые системы, используя преимущества декораторов. Когда сообщения отправляются от кого-то из сотрудников компании на внешний адрес, почтовый сервер добавляет к сообщению информацию об авторских правах, конфиденциальности, контактах. До тех пор, пока сообщение остается внутренним, эта информация не прикрепляется. Такое декорирование позволяет самому сообщению оставаться неизменным до тех пор, пока во время выполнения не будет принято решение обернуть сообщение дополнительной информацией.
Структурный шаблон «Фасад»
Назначение
Предоставляет единый интерфейс для набора различных интерфейсов в системе.
Когда использовать
- Если необходим простой интерфейс для предоставления доступа к сложной системе.
- Существует множество зависимостей между реализациями системы и клиентами.
- Системы и подсистемы должны быть многоуровневыми.
Структура
Фасад (Facade) предоставляет быстрый доступ к определенной функциональности подсистемы. Он знает, каким классам нужно переадресовать запрос, и какие данные для этого нужны.
Сложная система (Complex System) состоит из множества разнообразных классов. Для того чтобы заставить их что-то делать, Фасаду нужно знать подробности устройства подсистемы, порядок инициализации объектов и так далее.
Пример
Используя набор функциональных возможностей веб-сервиса, клиентский код должен взаимодействовать с простым интерфейсом, не беспокоясь о сложных взаимосвязях, которые могут существовать или не существовать за фасадом веб-сервиса. Один вызов веб-сервиса для обновления системы может фактически включать в себя взаимодействие с несколькими базами данных и сторонними сервисами, однако эта деталь скрыта благодаря реализации шаблона «Фасад».
Структурный шаблон «Легковес»
Назначение
Упрощает и повышает эффективность повторного использования многочисленных мелких объектов.
Когда использовать
- Если используется множество одинаковых объектов, что ведет к повышенному потреблению ресурсов.
- Большую часть состояния каждого объекта можно сделать внешним.
- Несколько совместно используемых объектов могут заменить большую группу.
- Идентичность каждого объекта не имеет значения.
Структура
Фабрика легковесов (Flyweight Factory) управляет созданием и повторным использованием легковесов. Легковес (Flyweight) содержит состояние, которое повторялось во множестве первоначальных объектов.
Клиент (Client) вычисляет или хранит контекст, то есть внешнее состояние конкретных легковесов (Concrete Flyweight).
Пример
Визуальные конструкторы веб-страниц нуждаются в отслеживании большого количества кнопок, страниц и других элементов, которые практически идентичны друг другу. Если сделать эти элементы «Легковесами», то все экземпляры каждого объекта смогут совместно использовать внутреннее состояние, сохраняя при этом внешнее состояние отдельно. Внутреннее состояние будет хранить общие свойства, например, то, как выглядит текстовое поле, сколько данных оно может вместить и какие события оно отображает. Внешнее состояние хранит неразделяемые свойства, такие как местоположение элемента, реакция на щелчок пользователя и обработка событий.
Структурный шаблон «Заместитель»
Назначение
Позволяет контролировать доступ на уровне объекта, действуя как дублер или объект-заместитель.
Когда использовать
- Если заменяемый объект является внешним по отношению к системе.
- Объекты должны создаваться по требованию.
- Требуется контроль доступа к исходному объекту.
- Нужна дополнительная функциональность при обращении к объекту.
Структура
Клиент (Client) работает с объектами через интерфейс сервиса. Благодаря этому, ему можно предложить объект заместителя вместо реального объекта (Real Subject) сервиса.
Заместитель (Proxy) хранит ссылку на объект сервиса. После завершения работы заместитель передает вызовы вложенному сервису.
Пример
Бухгалтерские приложения часто предоставляют пользователям возможность сверять банковские выписки с данными главной книги, автоматизируя большую часть процесса. Сама операция связи с третьей стороной является относительно ресурсоемкой. Используя прокси для представления объекта связи, можно ограничить количество вызовов или интервалы между ними. Кроме того, можно обернуть инстанцирование сложного объекта связи внутри класса прокси, отделяя вызывающий код от деталей реализации.
Материалы по теме
- Шаблоны проектирования в Python: для стильного кода
- Шаблоны проектирования в JavaScript простыми словами
- Шаблоны проектирования по-человечески: 6 порождающих паттернов, которые упростят жизнь