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.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ