Builder
Будівельник — породжувальний патерн, який дозволяє створювати складні об'єкти покроково. Будівельник дає можливість використовувати один і той самий код будівництва для отримання різних представлень об'єктів.
classDiagram
direction LR
class Director {
-builder
+construct_sports_car()
+construct_suv()
}
class Builder {
<<Interface>>
+reset()
+set_engine()
+set_wheels()
+get_result()
}
class CarBuilder {
+set_engine()
+set_wheels()
+get_result()
}
class CarManualBuilder {
+set_engine()
+set_wheels()
+get_result()
}
Director --> Builder
CarBuilder ..|> Builder
CarManualBuilder ..|> Builder
Named Arguments vs Real Builder
У Java патерн Builder часто використовують просто для того, щоб позбутися конструкторів із 10 аргументами. У Python цю проблему вирішено на рівні мови за допомогою іменованих аргументів:
Car(color="red", engine="V8", gps=True) — це зручно та читабельно.
Але справжній патерн Builder потрібен тоді, коли процес створення є складним і складається з етапів. Найкращий приклад, який ви знаєте — Django ORM:
User.objects.filter(is_active=True).exclude(role='guest').order_by('-date_joined')
Тут QuerySet виступає в ролі Будівельника. Ви покроково «налаштовуєте» майбутній SQL-запит. І лише наприкінці, коли дані дійсно потрібні (метод get_result у патерні або ітерація в Django), об'єкт «будується» і виконується запит до БД.
Lazy Initialization
Відкладена (лінива) ініціалізація — це прийом, коли ми відкладаємо створення важкого об'єкта або виконання тривалої операції доти, доки результат нам дійсно не знадобиться.
Логіка Lazy Initialization
flowchart LR
Start([Запит атрибута]) --> Check{Вже створено?}
Check -- Так --> Return[Повернути збережене значення]
Check -- Ні --> Calc[Виконати важку операцію]
Calc --> Save[Зберегти результат]
Save --> Return
У веброзробці це критично важливо. Уявіть, що під час запуску Django-проєкту ми б одразу завантажували всі дані з усіх таблиць БД у пам'ять. Сервер би стартував годину. Замість цього ми використовуємо ліниве завантаження.
Python-way: @cached_property
У Python цей патерн реалізується неймовірно елегантно через декоратор зі стандартної бібліотеки functools:
from functools import cached_property
class UserProfile:
@cached_property
def heavy_statistics(self):
print("Рахую складну статистику...")
return 42
p = UserProfile()
print(p.heavy_statistics) # Виконає код і збереже 42
print(p.heavy_statistics) # Поверне одразу 42, не виконуючи код
Переваги:
- Прискорює старт застосунку (не виконуємо зайву роботу одразу).
- Заощаджує ресурси, якщо важке поле так і не знадобилося користувачеві.
Object Pool
Об'єктний пул (Object Pool) — патерн, який зберігає набір уже ініціалізованих об'єктів, готових до роботи. Замість того щоб створювати об'єкт з нуля (що довго), ми беремо його з пулу, використовуємо і повертаємо назад.
sequenceDiagram
participant Client
participant Pool
participant Database
Client->>Pool: Дай з'єднання!
alt У пулі є вільне
Pool-->>Client: Ось, тримай (Connection #1)
else Пул порожній
Pool->>Database: Створити нове з'єднання
Database-->>Pool: Connection #2
Pool-->>Client: Ось, тримай (Connection #2)
end
Client->>Database: SQL Query...
Client->>Pool: Я все, забирай (Connection #1)
У Python створення звичайних об'єктів (рядків, списків, класів) працює дуже швидко, і пул для них не потрібен (про це дбає збирач сміття). Але є ресурси, створення яких коштує дуже дорого:
- З'єднання з базою даних: Відкриття TCP-з'єднання, авторизація, рукостискання (handshake). Це займає мілісекунди. Якщо відкривати з'єднання на кожен запит, сайт буде гальмувати. Тому SQLAlchemy використовує Connection Pool.
- Потоки та процеси: Створення нового процесу операційної системи — важка операція. Тому ми використовуємо
ThreadPoolExecutorабо воркери Celery/Gunicorn. Вони створюються один раз під час старту і потім просто обробляють завдання з черги.
Підсумок: використовуйте пул, коли вартість створення об'єкта («ціна квитка») занадто висока, щоб платити її щоразу.
Пам'ятайте про GIL: пули потоків гарні для I/O операцій, а для обчислень краще використовувати пули процесів. Це продемонструє вашу технічну грамотність.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ