JavaRush /Курси /Architecture & Logic /Поведінкові патерни, частина 2

Поведінкові патерни, частина 2

Architecture & Logic
Рівень 2 , Лекція 1
Відкрита

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-запит:

  1. Спочатку SecurityMiddleware перевіряє HTTPS.
  2. Потім SessionMiddleware шукає куки сесії.
  3. Потім AuthenticationMiddleware шукає користувача.
  4. І лише в кінці керування доходить до вашого 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. Ми просто «заморожуємо» об'єкт у байти і зберігаємо їх.

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