Динамічне програмування
Динамічне програмування (dynamic programming) — все, що визначається не під час компіляції, а в runtime: імена таблиць, полів, методів, типи обʼєктів. Використовується там, де структура роботи залежить від конфігурації, метаданих або вводу користувача — фреймворки, універсальні утиліти, конвертери даних.
Ціна гнучкості: компілятор уже не ловить помилок у іменах, перевірки зсуваються на runtime. Тому динаміку застосовуй тільки там, де статичний код не підходить.
Field symbols
Section titled “Field symbols”Field symbol — “псевдонім” на обʼєкт даних. Не копія, не вказівник у сенсі C — це alias: будь-який доступ до <fs> транслюється у доступ до прив’язаного обʼєкта.
FIELD-SYMBOLS: <fs> TYPE i, " типізований <fs_any> TYPE any, " узагальнений <fs_tab> TYPE ANY TABLE, " узагальнена таблиця <fs_str> TYPE any TABLE.
DATA num TYPE i VALUE 42.ASSIGN num TO <fs>.<fs> = 100. " змінює num — це aliasWRITE num. " 100TYPE any — узагальнений тип: до field symbol можна привʼязати що завгодно, але операції доступні обмежено (не всі оператори працюють з any).
Перевірка привʼязки
Section titled “Перевірка привʼязки”IF <fs> IS ASSIGNED. " Використовувати безпечноENDIF.
UNASSIGN <fs>. " відвʼязатиДинамічний ASSIGN
Section titled “Динамічний ASSIGN”Привʼязка за рядком з іменем:
DATA(tab_name) = `MARA`.ASSIGN (tab_name) TO <fs>. " привʼязатись до глобальної таблиці MARA
" Динамічний компонент структуриDATA(field) = `MATNR`.ASSIGN COMPONENT field OF STRUCTURE wa TO <fs>.
" Через числовий індексASSIGN COMPONENT 2 OF STRUCTURE wa TO <fs>.
" З ELSE UNASSIGN — якщо не вдалосьASSIGN (name) TO <fs> ELSE UNASSIGN.
" Звуження типу під час привʼязкиASSIGN obj->data TO <fs> CASTING TYPE ty_struct.Перевіряй sy-subrc після ASSIGN — або використовуй ELSE UNASSIGN і потім IS ASSIGNED.
Field symbol у циклах
Section titled “Field symbol у циклах”Типовий спосіб модифікувати рядки таблиці на місці — без зайвих копій:
LOOP AT itab ASSIGNING FIELD-SYMBOL(<row>). <row>-status = 'X'. " змінює рядок напряму у таблиціENDLOOP.Data references
Section titled “Data references”Data reference — справжнє посилання (вказівник) на обʼєкт даних. На відміну від field symbol, це повноцінна змінна: можна зберігати, передавати у методи, тримати в таблиці.
DATA: dref TYPE REF TO i, " типізоване посилання dref_any TYPE REF TO data. " узагальнене
DATA num TYPE i VALUE 42.GET REFERENCE OF num INTO dref." або через оператор:dref = REF #( num ).
dref->* = 100. " розіменування; змінює num->* — розіменування, дає доступ до значення.
Створення анонімних обʼєктів
Section titled “Створення анонімних обʼєктів”Data reference може вказувати на обʼєкт, створений у heap, без імені:
" ТипізованеDATA(int_ref) = NEW i( 42 ).
" Динамічне створення за іменем типуDATA gref TYPE REF TO data.CREATE DATA gref TYPE (type_name). " type_name = `MARA` наприкладCREATE DATA gref TYPE TABLE OF (type_name).CREATE DATA gref LIKE wa.
" Звуження до типової форми — через ASSIGN з field symbolASSIGN gref->* TO FIELD-SYMBOL(<fs>).Узагальнені data references
Section titled “Узагальнені data references”TYPE REF TO data — на будь-що. Для доступу потрібен field symbol:
DATA gref TYPE REF TO data.CREATE DATA gref TYPE ('MARA').
FIELD-SYMBOLS <wa> TYPE any.ASSIGN gref->* TO <wa>.
" Далі з <wa> працюємо як зі звичайною структуроюASSIGN COMPONENT 'MATNR' OF STRUCTURE <wa> TO FIELD-SYMBOL(<matnr>).Динамічний ABAP SQL
Section titled “Динамічний ABAP SQL”DATA: tab_name TYPE string VALUE `MARA`, where TYPE string VALUE `MTART = 'FERT'`, cols TYPE string VALUE `MATNR, MTART`, result TYPE REF TO data.
CREATE DATA result TYPE TABLE OF (tab_name).ASSIGN result->* TO FIELD-SYMBOL(<tab>).
SELECT (cols) FROM (tab_name) WHERE (where) INTO TABLE @<tab>.
" Динамічний ORDER BYSELECT * FROM (tab_name) ORDER BY (sort_cols) INTO TABLE @<tab>.Динамічний виклик методу
Section titled “Динамічний виклик методу”DATA(method_name) = `CALC`.
" Через операторCALL METHOD obj->(method_name) EXPORTING a = 1 b = 2 RECEIVING result = DATA(r).
" Зі списком параметрів у таблиціDATA params TYPE abap_parmbind_tab.params = VALUE #( ( name = 'A' kind = cl_abap_objectdescr=>exporting value = REF #( v1 ) ) ( name = 'B' kind = cl_abap_objectdescr=>exporting value = REF #( v2 ) ) ).
CALL METHOD obj->(method_name) PARAMETER-TABLE params.Static-метод:
CALL METHOD (class_name)=>(method_name) ... .Runtime Type Services (RTTS)
Section titled “Runtime Type Services (RTTS)”RTTS — набір класів для роботи з типами у runtime. Три підмножини:
- RTTI (Runtime Type Identification) — дізнатись тип обʼєкта.
- RTTC (Runtime Type Creation) — створити тип і data object за описом.
- RTTS — загальна назва.
RTTI: отримати опис типу
Section titled “RTTI: отримати опис типу”DATA num TYPE p LENGTH 10 DECIMALS 2.DATA(type_desc) = cl_abap_typedescr=>describe_by_data( num ).
WRITE type_desc->type_kind. " тип (c, p, i...)WRITE type_desc->absolute_name. " повне імʼя типуІєрархія дескрипторів:
cl_abap_typedescr— коріньcl_abap_elemdescr— елементарні (i, c, p, string…)cl_abap_complexdescrcl_abap_structdescr— структуриcl_abap_tabledescr— внутрішні таблиці
cl_abap_refdescr— reference-типиcl_abap_objectdescrcl_abap_classdescr— класиcl_abap_intfdescr— інтерфейси
Типовий даункаст:
DATA(td) = cl_abap_typedescr=>describe_by_data( some_struct ).IF td->kind = cl_abap_typedescr=>kind_struct. DATA(sd) = CAST cl_abap_structdescr( td ). LOOP AT sd->components INTO DATA(comp). WRITE: / comp-name, comp-type_kind. ENDLOOP.ENDIF.Опис за ім’ям
Section titled “Опис за ім’ям”" За імʼям data-елемента / таблиціDATA(td) = cl_abap_typedescr=>describe_by_name( 'MARA' ).
" За імʼям класу / інтерфейсаDATA(cd) = CAST cl_abap_classdescr( cl_abap_typedescr=>describe_by_name( 'ZCL_DEMO' ) ).RTTC: динамічне створення типу і обʼєкта
Section titled “RTTC: динамічне створення типу і обʼєкта”" Опис елементарного типуDATA(elem) = cl_abap_elemdescr=>get_c( 10 ). " c length 10
" Опис структури зі списком компонентівDATA comps TYPE cl_abap_structdescr=>component_table.comps = VALUE #( ( name = 'ID' type = cl_abap_elemdescr=>get_i( ) ) ( name = 'NAME' type = cl_abap_elemdescr=>get_string( ) ) ).
DATA(struct_td) = cl_abap_structdescr=>create( comps ).DATA(table_td) = cl_abap_tabledescr=>create( struct_td ).
" Створення обʼєкта за описомDATA dref TYPE REF TO data.CREATE DATA dref TYPE HANDLE table_td.
ASSIGN dref->* TO FIELD-SYMBOL(<tab>)." <tab> тепер — внутрішня таблиця з описаним типомБезпека у динамічному коді
Section titled “Безпека у динамічному коді”- Ніколи не кидай імена обʼєктів з користувацького вводу у SQL/ASSIGN без валідації. Мінімум — білий список.
- Для CDS/database-обʼєктів додатково виконуй authorization check.
- Використовуй сервіс перевірки імен:
cl_abap_dyn_prg— методи типуcheck_variable_name,check_table_name_str.
" Приклад санітизаціїDATA(safe_name) = cl_abap_dyn_prg=>check_table_name_str( val = input_name packages = 'Z_MY_PKG' ).Коли динаміка виправдана
Section titled “Коли динаміка виправдана”- Універсальні утиліти: logger, serializer, generic copy.
- Фреймворки RAP, CDS, BAdI — там сам ABAP дає динамічне API.
- Конвертери: JSON/XML ↔ структура невідомого типу.
- Тулзи для міграцій, репортів з налаштовуваним списком полів.
Адаптовано з 06_Dynamic_Programming.md (Apache 2.0). Повний перелік нюансів — в оригіналі.