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

Динамічне програмування

Динамічне програмування (dynamic programming) — все, що визначається не під час компіляції, а в runtime: імена таблиць, полів, методів, типи обʼєктів. Використовується там, де структура роботи залежить від конфігурації, метаданих або вводу користувача — фреймворки, універсальні утиліти, конвертери даних.

Ціна гнучкості: компілятор уже не ловить помилок у іменах, перевірки зсуваються на runtime. Тому динаміку застосовуй тільки там, де статичний код не підходить.

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 — це alias
WRITE num. " 100

TYPE any — узагальнений тип: до field symbol можна привʼязати що завгодно, але операції доступні обмежено (не всі оператори працюють з any).

IF <fs> IS ASSIGNED.
" Використовувати безпечно
ENDIF.
UNASSIGN <fs>. " відвʼязати

Привʼязка за рядком з іменем:

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.

Типовий спосіб модифікувати рядки таблиці на місці — без зайвих копій:

LOOP AT itab ASSIGNING FIELD-SYMBOL(<row>).
<row>-status = 'X'. " змінює рядок напряму у таблиці
ENDLOOP.

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 symbol
ASSIGN gref->* TO FIELD-SYMBOL(<fs>).

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>).
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 BY
SELECT * 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) ... .

RTTS — набір класів для роботи з типами у runtime. Три підмножини:

  • RTTI (Runtime Type Identification) — дізнатись тип обʼєкта.
  • RTTC (Runtime Type Creation) — створити тип і data object за описом.
  • RTTS — загальна назва.
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_complexdescr
      • cl_abap_structdescr — структури
      • cl_abap_tabledescr — внутрішні таблиці
    • cl_abap_refdescr — reference-типи
    • cl_abap_objectdescr
      • cl_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.
" За імʼям 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). Повний перелік нюансів — в оригіналі.