JavaRush /Курсы /Architecture & Logic /Структурные паттерны

Структурные паттерны

Architecture & Logic
1 уровень , 1 лекция
Открыта

Adapter

Адаптер — это структурный паттерн, который позволяет объектам с несовместимыми интерфейсами работать вместе. Представьте, что вы приехали в страну с другим типом розеток. Чтобы зарядить ноутбук, вам нужен переходник. В коде роль переходника выполняет Адаптер.

Схема паттерна Adapter

    classDiagram
        direction LR
        class Client {
            +uses()
        }
        class TargetInterface {
            <<Interface>>
            +expected_method()
        }
        class Adapter {
            +expected_method()
        }
        class LegacyClass {
            +weird_method_name()
        }

        Client --> TargetInterface
        Adapter ..|> TargetInterface
        Adapter --> LegacyClass : calls weird_method_name()
    

Duck Typing

В статических языках Java, C++ адаптеры нужны постоянно, чтобы "подогнать" классы под строгие типы интерфейсов. В Python всё проще: если объект "крякает как утка" (имеет нужный метод), мы считаем его уткой.

Тем не менее, Адаптер полезен, когда вы используете стороннюю библиотеку, и имена её методов не совпадают с тем, что ожидает ваш код. Вместо того чтобы переписывать весь свой код, вы создаете класс-обертку.

Сильные стороны:

  • Single Responsibility Principle: вы отделяете код конвертации данных от основной бизнес-логики.
  • Open/Closed Principle: можно внедрять новые типы адаптеров без изменения существующего кода.

Decorator

Декоратор — это паттерн, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».

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

    classDiagram
        direction BT
        %% Базовый интерфейс
        class Notifier {
            <<Interface>>
            +send(message)
        }

        %% Реальный компонент
        class EmailNotifier {
            +send(message)
        }

        %% Базовый декоратор
        class BaseDecorator {
            -wrappee: Notifier
            +send(message)
        }

        %% Конкретные декораторы
        class SMSDecorator {
            +send(message)
        }
        class SlackDecorator {
            +send(message)
        }

        %% Связи
        EmailNotifier ..|> Notifier
        BaseDecorator ..|> Notifier
        BaseDecorator o-- Notifier : Оборачивает (Has-A)
        SMSDecorator --|> BaseDecorator
        SlackDecorator --|> BaseDecorator
    

Встроенный синтаксис

Python — один из немногих языков, где Декоратор — это не просто паттерн из книги, а встроенная конструкция языка. Вы используете их каждый день:

  • @login_required в Django — проверяет авторизацию перед выполнением view-функции.
  • @app.get("/") в FastAPI — регистрирует функцию как обработчик маршрута.
  • @staticmethod — изменяет поведение метода внутри класса.

В Python декоратор — это просто функция, которая принимает другую функцию и возвращает новую (обернутую). Это намного элегантнее, чем создавать громоздкие классы-обертки, как это делается в Java.

Proxy

Заместитель — это объект, который выступает в роли «прослойки» между клиентом и реальным сервисным объектом. Прокси получает вызов от клиента, выполняет какую-то промежуточную работу (проверку доступа, кэширование, логгирование), а затем передает вызов реальному объекту.

    classDiagram
        direction LR
        class Client

        %% Интерфейс
        class YouTubeLib {
            <<Interface>>
            +download_video(id)
        }

        %% Реальный сервис (Тяжелый)
        class RealDownloader {
            +download_video(id)
        }

        %% Прокси (Легкий / Умный)
        class CacheProxy {
            -real_service: RealDownloader
            -cache: dict
            +download_video(id)
        }

        Client --> YouTubeLib
        RealDownloader ..|> YouTubeLib
        CacheProxy ..|> YouTubeLib

        %% Главная связь: Прокси управляет Реальным сервисом
        CacheProxy --> RealDownloader : Делегирует, если нет в кэше
    

В Python вы часто встречаетесь с Proxy, даже не замечая этого:

  • Lazy Loading в Django ORM: когда вы делаете запрос user.groups.all(), Django не загружает данные из базы мгновенно. Он создает объект-прокси. Реальный запрос SQL выполняется только тогда, когда вы попытаетесь прочитать данные.
  • Mock-объекты в тестах: библиотека unittest.mock создает прокси-объекты, которые притворяются реальными классами, чтобы вы могли изолированно протестировать код.

Виды прокси:

  • Виртуальный прокси (Lazy Loading) откладывает создание тяжелого объекта до момента, когда он действительно понадобится.
  • Кэширующий прокси хранит результаты вычислений и отдает их, если такой запрос уже был.
  • Защищающий прокси проверяет права доступа перед тем, как пустить клиента к реальному объекту.

Bridge

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

    classDiagram
        direction TB
        class Shape {
            -renderer: Renderer
            +draw()
        }
        class Circle {
            +draw()
        }
        class Square {
            +draw()
        }
        class Renderer {
            <<Interface>>
            +render_circle()
            +render_square()
        }
        class VectorRenderer {
            +render_circle()
            +render_square()
        }
        class RasterRenderer {
            +render_circle()
            +render_square()
        }

        Circle --|> Shape
        Square --|> Shape
        VectorRenderer ..|> Renderer
        RasterRenderer ..|> Renderer
        Shape o-- Renderer : Агрегация
    

Классический пример: у нас есть класс Фигура (Круг, Квадрат) и способ её рисования (Векторный, Растровый). Если использовать обычное наследование, нам придется создавать классы: VectorCircle, RasterCircle, VectorSquare, RasterSquare. Количество классов растет как произведение $N \times M$.

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

В Python это часто реализуется через внедрение зависимостей (Dependency Injection): мы просто передаем нужную реализацию в конструктор класса.

Facade

Фасад — это паттерн, который предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.

    classDiagram
        direction LR
        class Client
        class Facade {
            +simple_method()
        }
        class ComplexSystemA
        class ComplexSystemB
        class ComplexSystemC

        Client --> Facade
        Facade --> ComplexSystemA
        Facade --> ComplexSystemB
        Facade --> ComplexSystemC
    

Вы пользуетесь фасадами каждый день. Отличный пример — библиотека Requests в Python.

Внутри Python есть стандартный модуль urllib3. Он мощный, но очень сложный и низкоуровневый. Библиотека requests — это Фасад, который предоставляет вам простой и понятный метод requests.get(), скрывая внутри себя сотни строк кода настройки соединений, кодировок и заголовков.

Когда применять:

  • Когда нужно предоставить простой интерфейс к сложной подсистеме.
  • Когда вы хотите разложить подсистему на слои (Фасад может служить точкой входа для определенного уровня приложения).

Composite

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

    classDiagram
        direction BT
        class FileSystemComponent {
            <<Interface>>
            +show_details()
        }
        class File {
            +show_details()
        }
        class Folder {
            -children: list
            +add(component)
            +show_details()
        }

        File ..|> FileSystemComponent
        Folder ..|> FileSystemComponent
        Folder o-- FileSystemComponent : Содержит
    

Лучший пример — файловая система. У вас есть «Файлы» простые объекты и «Папки» составные объекты. Папка может содержать файлы и другие папки. Но для пользователя (или операционной системы) и папка, и файл — это просто «иконка», которую можно открыть, переименовать или переместить.

Суть паттерна:

Создается общий интерфейс Component. Лист (Leaf) реализует его напрямую. Контейнер (Composite) реализует его, пробегая циклом по всем своим детям и вызывая этот метод у них.

В веб-разработке Composite часто встречается при создании меню или при рендеринге HTML.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ