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

Iterator

Iterator — поведінковий патерн, що дає універсальний спосіб обходити колекцію, не розкриваючи, як саме вона зберігає дані. Клієнт отримує обʼєкт-ітератор з методами has_next( ) / next( ) і не замислюється, чи це standard table, hashed table, дерево чи курсор на БД.

Пульт телевізора з кнопкою «наступний канал». Тобі неважливо, як телевізор всередині зберігає список каналів, відсортований він чи ні, з кабелю чи ефіру — ти просто натискаєш «next», і отримуєш наступний.

  • Колекція має нетривіальну структуру (дерево, граф, курсор на БД, потік файлу) — прямий LOOP AT неможливий або незручний.
  • Потрібно кілька активних обходів одної колекції одночасно (два ітератори не заважають один одному).
  • Хочеш приховати внутрішнє представлення — клієнт працює тільки через ітератор.
  • Послідовність обходу залежить від стратегії (сортування, фільтр) — ітератор інкапсулює стратегію.
INTERFACE zif_iterator.
METHODS:
has_next RETURNING VALUE(rv) TYPE abap_bool,
next RETURNING VALUE(rr) TYPE REF TO data
RAISING zcx_iterator_exhausted.
ENDINTERFACE.
" Колекція, що вміє видавати ітератор
INTERFACE zif_iterable.
METHODS create_iterator RETURNING VALUE(ro) TYPE REF TO zif_iterator.
ENDINTERFACE.
" Конкретна колекція — дерево
CLASS zcl_tree DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_iterable.
METHODS:
add_node IMPORTING iv_value TYPE string.
PRIVATE SECTION.
DATA mt_nodes TYPE STANDARD TABLE OF zty_node WITH EMPTY KEY.
ENDCLASS.
" Ітератор — окремий клас, що тримає позицію
CLASS zcl_tree_iterator DEFINITION PUBLIC FINAL CREATE PRIVATE
FRIENDS zcl_tree.
PUBLIC SECTION.
INTERFACES zif_iterator.
PRIVATE SECTION.
METHODS constructor IMPORTING io_tree TYPE REF TO zcl_tree.
DATA:
mo_tree TYPE REF TO zcl_tree,
mv_pos TYPE i.
ENDCLASS.

Використання — як у GoF:

DATA(lo_it) = lo_tree->zif_iterable~create_iterator( ).
WHILE lo_it->has_next( ) = abap_true.
DATA(lr_value) = lo_it->next( ).
" ...
ENDWHILE.

У 95% випадків ABAP не потребує явного Iterator-патерну — LOOP AT itab і так універсальний. Іди у Iterator лише коли:

  • внутрішня структура — НЕ таблиця (дерево, граф, курсор);
  • структура НЕ має бути видимою клієнту (інкапсуляція);
  • ітерація має власний стан (позиція), що переживає між викликами методів.
  • iXML API (if_ixml_node_iterator, if_ixml_node_collection) — канонічний Iterator у SAP-стандарті: обхід DOM-дерева без оголення структури.
  • cl_abap_typedescr, cl_abap_structdescr — отримання компонентів через get_components( ) повертає таблицю; для DDIC-дерев iterator-подібний API.
  • Cursor-based SELECT (OPEN CURSOR, FETCH NEXT CURSOR) — по суті Iterator над БД-результатом.
  • Streams (CL_ABAP_GZIP, файлові потоки) — ітератор над байтами.
  • Одночасна модифікація колекції під час ітерації. Якщо клієнт додає елемент у колекцію під час обходу — ітератор може впасти або дати невалідні дані. Або блокуй модифікації на час ітерації, або явно документуй поведінку (fail-fast vs fail-safe).
  • Повторний обхід. Деякі ітератори — одноразові (курсор на БД закрився після FETCH). Клієнт думає, що може запустити has_next знову з початку, а отримує порожньо. Документуй контракт.
  • RETURNING VALUE(rr) TYPE REF TO data — ABAP не дає generics, тож ітератор над типізованою таблицею або віддає загальний REF TO data (треба ASSIGN), або — один клас-ітератор на один тип колекції (громіздко).
  • Витоки. Якщо клієнт не дійшов до кінця ітерації і кинув ітератор — ресурси (курсор, handle на файл) можуть не звільнитись. Використовуй TRY ... FINALLY-еквівалент через CLEANUP або явний close( ).

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

Section titled “Коли НЕ використовувати”
  • Дані в STANDARD TABLE — пиши LOOP AT і не вигадуй.
  • Потрібно індексоване читання, а не послідовне — READ TABLE ... INDEX.
  • Одна проста колекція, один клієнт, видимість структури не турбує — Iterator додасть шуму без вигоди.