State
Стан — патерн, який дозволяє об'єкту змінювати свою поведінку при зміні його внутрішнього стану. Ззовні здається, що змінився клас об'єкта.
stateDiagram-v2
[*] --> Draft
Draft --> Moderation : send_to_review()
Moderation --> Published : approve()
Moderation --> Draft : reject()
Published --> [*]
note right of Draft
У чернетці не можна
публікувати напряму.
end note
Уявіть документ. У стані Draft метод publish() відправляє його на модерацію. У стані Moderation цей же метод може викинути помилку "Вже на перевірці". А в Published — нічого не зробить.
Суть патерна.
Замість величезного if-elif-else (якщо статус такий-то, роби те-то), ми виносимо поведінку кожного стану в окремий клас. Об'єкт документа просто зберігає посилання на поточний стан self.state і делегує йому роботу.
Python-way: у простих випадках достатньо бібліотеки django-fsm або звичайного поля status у моделі. Повноцінні класи-стани потрібні тоді, коли логіка переходів дуже складна.
Strategy
Стратегія — патерн, який визначає сімейство алгоритмів, інкапсулює їх і робить взаємозамінними. Це дозволяє обирати алгоритм «на льоту», не змінюючи код, який його використовує.
classDiagram
direction LR
class Context {
-strategy
+set_strategy(s)
+execute()
}
class PaymentStrategy {
<<Interface>>
+pay(amount)
}
class PayPal {
+pay(amount)
}
class CreditCard {
+pay(amount)
}
Context o-- PaymentStrategy
PayPal ..|> PaymentStrategy
CreditCard ..|> PaymentStrategy
Приклад
Оплата в інтернет-магазині. Користувач обирає PayPal або Картку. Код оформлення замовлення залишається незмінним, змінюється лише об'єкт стратегії оплати.
Функції як стратегії
У класичному ООП ви зобов'язані створювати класи PayPalStrategy, CardStrategy. У Python функції — це об'єкти. Ви можете просто передати функцію сортування або оплати як аргумент:
# Замість класів
def pay_by_paypal(amount): ...
def pay_by_card(amount): ...
# Використання
process_payment(100, strategy=pay_by_paypal)
Згадайте стандартну функцію sorted(data, key=...). Аргумент key — це і є Стратегія вибору ключа сортування.
Template Method
Шаблонний метод — патерн, який визначає скелет алгоритму в суперкласі, але дозволяє підкласам перевизначати конкретні кроки цього алгоритму без зміни його структури.
classDiagram
direction BT
class DataMiner {
+mine()
#open_file()
#extract_data()
#parse_data()
#close_file()
}
class PDFMiner {
#extract_data()
#parse_data()
}
class CSVMiner {
#extract_data()
#parse_data()
}
PDFMiner --|> DataMiner
CSVMiner --|> DataMiner
note for DataMiner "Метод mine() викликає кроки:
open -> extract -> parse -> close.
Це і є шаблон."
Це класичне успадкування. Ви пишете базовий клас із загальною логікою ("відкрити файл, прочитати, закрити"), а специфічні частини ("як саме читати PDF") залишаєте абстрактними методами, які реалізують нащадки.
У Python: активно використовується у фреймворках. Наприклад, Django Class-Based Views. Метод dispatch() — це шаблонний метод, який викликає get() або post() залежно від запиту.
Chain of Responsibility
Ланцюжок відповідальності — патерн, що дозволяє передавати запит ланцюжком потенційних обробників, поки один із них не обробить запит.
sequenceDiagram
participant Client
participant AuthMiddleware
participant LogMiddleware
participant View
Client->>AuthMiddleware: Запит /profile
alt Не авторизований
AuthMiddleware-->>Client: 403 Forbidden
else Авторизований
AuthMiddleware->>LogMiddleware: Передає далі
LogMiddleware->>View: Передає далі
View-->>LogMiddleware: Відповідь HTML
LogMiddleware-->>AuthMiddleware: Відповідь HTML
AuthMiddleware-->>Client: Відповідь HTML
end
Python-way: Middleware!
Вам не потрібно шукати абстрактні приклади. Django Middleware та FastAPI Dependencies — це і є реалізація Ланцюжка відповідальності.
Коли приходить HTTP-запит:
- Спочатку
SecurityMiddlewareперевіряє HTTPS. - Потім
SessionMiddlewareшукає куки сесії. - Потім
AuthenticationMiddlewareшукає користувача. - І лише в кінці керування доходить до вашого
View.
Кожна ланка може або передати запит далі, або повернути відповідь (перервати ланцюжок).
Memento
Зберігач — патерн, який дозволяє зберегти та відновити минулий стан об'єкта, не розкриваючи подробиць його реалізації.
classDiagram
direction LR
class Editor {
-content
+make_snapshot()
+restore(snapshot)
}
class Snapshot {
-state
+get_state()
}
class History {
-snapshots: list
+push(s)
+pop()
}
Editor ..> Snapshot : Створює
History o-- Snapshot : Зберігає
Це кнопка "Undo" Ctrl+Z у вашому редакторі. Редактор створює знімок (snapshot) перед кожною дією. Історія зберігає стек цих знімків. Якщо ви помилилися, ви дістаєте останній знімок і відновлюєте стан.
У Python часто реалізується через серіалізацію модулем pickle, json або за допомогою deepcopy. Ми просто «заморожуємо» об'єкт у байти і зберігаємо їх.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ