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

Замісник (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.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ