Перейти до вмісту

Prototype

Prototype — породжуючий патерн: замість створювати обʼєкт нуля (з важкою ініціалізацією — зверненнями до БД, RFC-викликами, побудовою дерев), ти клонуєш уже готовий зразок.

Шаблон договору: замість писати новий договір з нуля, ти береш готовий шаблон і підправляєш у ньому імена й суми. Так само з обʼєктом — клонуєш «заповнений» prototype і тільки коригуєш поля, що відрізняються.

  • Ініціалізація дорога: RFC-дзвінок, важкий SELECT, побудова обʼєктної структури (дерева XML, deeply nested structures).
  • Треба багато однотипних обʼєктів, що відрізняються в кількох полях.
  • Клієнту не треба знати конкретний клас — йому потрібен тільки метод clone( ).
  • Треба зробити snapshot стану, який потім може змінюватись незалежно.

ABAP не має вбудованого clone( ) як Java. Є варіанти:

Варіант 1 — власний clone( ) метод

Section titled “Варіант 1 — власний clone( ) метод”

Явний контракт через інтерфейс:

INTERFACE zif_cloneable.
METHODS clone RETURNING VALUE(ro_clone) TYPE REF TO zif_cloneable.
ENDINTERFACE.
CLASS zcl_request DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_cloneable.
METHODS:
constructor
IMPORTING iv_endpoint TYPE string
is_state TYPE zty_state OPTIONAL,
get_state RETURNING VALUE(rs_state) TYPE zty_state,
set_body IMPORTING iv_body TYPE string.
PRIVATE SECTION.
DATA:
mv_endpoint TYPE string,
ms_state TYPE zty_state.
ENDCLASS.
CLASS zcl_request IMPLEMENTATION.
METHOD constructor.
mv_endpoint = iv_endpoint.
IF is_state IS SUPPLIED.
ms_state = is_state.
ELSE.
" дорога ініціалізація — автентифікація, токен, etc
ms_state = do_expensive_init( ).
ENDIF.
ENDMETHOD.
METHOD zif_cloneable~clone.
" shallow copy через CORRESPONDING структур/полів
ro_clone = NEW zcl_request( iv_endpoint = me->mv_endpoint
is_state = me->ms_state ).
ENDMETHOD.
METHOD get_state. rs_state = ms_state. ENDMETHOD.
METHOD set_body. ms_state-body = iv_body. ENDMETHOD.
ENDCLASS.

Використання:

DATA(lo_prototype) = NEW zcl_request( iv_endpoint = '/api/sap/orders' ).
" десять однакових запитів з різним body — без повторної авторизації
DO 10 TIMES.
DATA(lo_req) = CAST zcl_request( lo_prototype->zif_cloneable~clone( ) ).
lo_req->set_body( build_body( sy-index ) ).
lo_req->send( ).
ENDDO.

Варіант 2 — глибоке копіювання через серіалізацію

Section titled “Варіант 2 — глибоке копіювання через серіалізацію”

Коли обʼєкт має складний стан з вкладеними обʼєктами — shallow copy не вистачить:

METHOD zif_cloneable~clone.
DATA lv_xml TYPE xstring.
" серіалізація через iXML або CALL TRANSFORMATION
CALL TRANSFORMATION id SOURCE src = me RESULT XML lv_xml.
CALL TRANSFORMATION id SOURCE XML lv_xml RESULT src = ro_clone.
ENDMETHOD.

Дорого, але гарантує повне відчеплення клону від оригіналу.

Варіант 3 — CORRESPONDING для DTO

Section titled “Варіант 3 — CORRESPONDING для DTO”

Якщо клонується не обʼєкт, а значення-структураCORRESPONDING + REF:

DATA(ls_copy) = CORRESPONDING ts_order( ls_original ).

Не патерн Prototype у GoF-сенсі, але той самий результат для простого випадку.

  • RAP / EMLMODIFY ENTITIES ... CREATE FROM фактично клонує стан сутності-прототипу.
  • Shared Memory Objects (cl_shm_area) — shared-памʼять часто зберігає прототипи для подальшого копіювання у user-session.
  • Deep copy через CL_ABAP_BROKER — у деяких версіях ABAP є helper для глибокого клонування обʼєктів.
  • Не забудь зробити final. Якщо клас має clone( ) і десь успадковується — нащадок може «забути» переозначити клон і повернути обʼєкт не того типу.
  • Equals і hash після клону. Якщо є кастомний equals( ), переконайся, що клон === оригінал за цим методом, або чітко задокументуй інакше.
  • Імутабельний прототип. Тримай eталонний prototype незмінним. Інакше всі наступні клони отримають підправлені значення, і в коді ніде не видно, коли саме відбулась зміна.

Коли НЕ використовувати

Section titled “Коли НЕ використовувати”
  • Ініціалізація дешева — звичайний NEW простіший і зрозуміліший.
  • Обʼєкти простому — CORRESPONDING над структурою.
  • Немає reference-полів — shallow copy тривіальна, патерн не потрібен.