Strategy
Strategy — поведінковий патерн, що дозволяє змінювати алгоритм у runtime без правки клієнта. Варіанти алгоритму — окремі класи, які реалізують спільний інтерфейс. Клієнт тримає посилання на інтерфейс і викликає його, не знаючи про конкретну реалізацію.
Метафора
Section titled “Метафора”Калькулятор знижки у інтернет-магазині: є знижка для постійних клієнтів, сезонна, за промокодом, немає знижки взагалі. Логіка в кожному випадку різна, але вхід і вихід однакові: сума → нова сума. Кошик покупок не повинен знати, яка саме знижка застосовується — він просить стратегію порахувати.
Коли застосовувати
Section titled “Коли застосовувати”- Є кілька варіантів обчислення одного й того самого: розрахунок знижки, алгоритм сортування, спосіб форматування.
IF/CASE-розгалуження за типом стає великим і тягнеться у кілька методів.- Варіант потрібно обирати у runtime, за користувачем, customizing-ом, або BAdI.
- Треба додавати нові варіанти без правки існуючого коду (OCP).
ABAP-реалізація
Section titled “ABAP-реалізація”" Контракт стратегіїINTERFACE zif_discount. METHODS calc IMPORTING iv_amount TYPE p iv_customer_id TYPE kunnr RETURNING VALUE(rv_final) TYPE p.ENDINTERFACE.
" Конкретні стратегіїCLASS zcl_discount_none DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_discount.ENDCLASS.CLASS zcl_discount_none IMPLEMENTATION. METHOD zif_discount~calc. rv_final = iv_amount. ENDMETHOD.ENDCLASS.
CLASS zcl_discount_vip DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_discount.ENDCLASS.CLASS zcl_discount_vip IMPLEMENTATION. METHOD zif_discount~calc. rv_final = iv_amount * '0.85'. ENDMETHOD.ENDCLASS.
CLASS zcl_discount_promo DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_discount. METHODS constructor IMPORTING iv_code TYPE string. PRIVATE SECTION. DATA mv_code TYPE string.ENDCLASS.
" Клієнт — знає тільки про інтерфейсCLASS zcl_cart DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS: constructor IMPORTING io_discount TYPE REF TO zif_discount, total RETURNING VALUE(rv_sum) TYPE p. PRIVATE SECTION. DATA: mo_discount TYPE REF TO zif_discount, mt_items TYPE TABLE OF zty_item.ENDCLASS.
CLASS zcl_cart IMPLEMENTATION. METHOD constructor. mo_discount = io_discount. ENDMETHOD. METHOD total. DATA(lv_raw) = REDUCE p( INIT s = 0 FOR ls IN mt_items NEXT s = s + ls-price * ls-qty ). rv_sum = mo_discount->calc( iv_amount = lv_raw iv_customer_id = mv_customer_id ). ENDMETHOD.ENDCLASS.Використання:
DATA lo_discount TYPE REF TO zif_discount.
CASE lv_customer_class. WHEN 'VIP'. lo_discount = NEW zcl_discount_vip( ). WHEN 'PROMO'. lo_discount = NEW zcl_discount_promo( iv_code = lv_code ). WHEN OTHERS. lo_discount = NEW zcl_discount_none( ).ENDCASE.
DATA(lo_cart) = NEW zcl_cart( io_discount = lo_discount ).Зміна правил знижки — додаєш новий клас (наприклад, zcl_discount_seasonal) і передаєш його у zcl_cart. Код zcl_cart не змінюється.
Patterns — сусіди
Section titled “Patterns — сусіди”- Strategy — алгоритм всередині одного методу, свобода вибору до виклику.
- State — поведінка залежить від стану обʼєкта, перемикається сам обʼєкт.
- Template Method — скелет фіксований, змінні кроки переозначаються у підкласі (статичний вибір).
Strategy та State виглядають майже однаково в коді. Різниця — у наміру: хто обирає реалізацію. Strategy — клієнт знає ззовні. State — обʼєкт перемикає себе зсередини.
SAP-специфіка
Section titled “SAP-специфіка”- BAdI (Business Add-In) — SAP-стандартний механізм strategy: стандартний код викликає BAdI-інтерфейс, замовник підсуває свою реалізацію через
CL_BADI_MANAGER. - BTE, BRF+ правила — стратегії, які обираються за customizing-ключем.
- RAP behavior implementation — кожна
determination/validation— потенційний strategy-інтерфейс.
Підводні камені
Section titled “Підводні камені”- Синдром Single-Strategy. Якщо стратегія завжди одна — не роби інтерфейс. Переробиш, коли знадобиться друга.
- Параметри у стратегій різні. Якщо
calcу різних стратегіях хоче різні вхідні дані — пакуй уTY_CONTEXT-структуру, або передавай клієнт-обʼєкт якio_cart. - Вибір стратегії розповзся. Якщо
CASE lv_type WHEN ...дублюється в 5 місцях — витягуй у фабрику стратегій. - Залежність від глобального стану. Стратегія не повинна лізти у
sy-*чи singleton-и — інакше тест неможливий.
Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Одна реалізація — прямий виклик коротший і зрозуміліший.
- Алгоритм —
IF/ELSEз двох рядків. Не роби клас на кожну мінливу примху. - Швидкість критична у тугому циклі — поліморфний виклик у ABAP дешевий, але не безплатний.