Link Search Menu Expand Document

“Слабое связывание” через хэндлеры

   Иногда ООП может довольно сильно усложнить структуру программы.
Если не идти в дебри проектирования, наследования, паттернов проектирования, лямбда выражений и прочего умного и сложного, часто требуется просто вызвать метод, который реализован совсем в другом месте, тем самым разделить код на более мелкие части, которые не сильно связаны между собой.
Для этого в ABAP есть два основных механизма для слабого связывания классов между собой интерфейсы и события.
Но как не городить огород и не усложнять себе жизнь с наследованием и интерфейсами и прочими плюшками ООП?

image





    Интерфейсы очень гибкий инструмент для разбиения кода на логические блоки, который позволяет делать код не таким связанным, в отличии от классы.
Но их имплементация в ABAP, в плане написания кода, дело довольно многословное и не всегда удобное. Также то обстоятельство что нужно имплементировать каждый метод в интерфейсе (если это только не класс тестирования) вводит дополнительные ограничения на их использование.

К примеру если у вас есть интерфейс по управлению экранами, вам нужно реализовать оба метода PAI и PBO.

Если PBO вам не нужен, вы делаете его пустым и потом еще пишете для ATC что он нужен (так себе удовольствие)

Тогда можно для гибкости объявить их событиями

image

И сделать что то вроде

    " Send container & current screen number
    RAISE EVENT zif_eui_manager~pbo_event
     EXPORTING
       io_container = io_container
       iv_dynnr = sy-dynnr.
...

    " Handler returns cv_close = 'X' to close SCREEN
    RAISE EVENT zif_eui_manager~pai_event
     EXPORTING
       iv_command = sy-ucomm
       cv_close = REF #( lv_close ).

Но что делать если вам нужно вызвать событие другого класса?
К примеру сделать вызов события из локально класса (Class relevant to local definitions), когда он реализует часть логику глобального класса.
RAISE EVENT можно вызывать только собственного класса.

Или к примеру если у вас есть обертка на ALV и вы хотите переслать событие от CL_GUI_ALV_GRID в другое место, те отловить для примера on_double_click, и потом делегировать выполнение другому классу.


ZCL_EUI_EVENT_CALLER

   Одно из простых решений это вызвать метод обработчика событий через dynamic call.
Те описание хэндлера можно представить в качестве интерфейса с методом который можно вызвать через CALL METHOD with PARAMETER-TABLE.

Когда в коде есть описание вызываемого метода, мы зная его сигнатуру можем вызвать его динамический

      on_hotspot_click FOR EVENT hotspot_click OF cl_gui_alv_grid
        IMPORTING
            sender
            e_row_id
            e_column_id,

Реализация

Для начала нужно знать что за метод нужно вызвать

image

  • IO_HANDLER - Объект чей метод нужно вызвать
  • IV_HANDLERS_MAP - Если у вас есть несколько обработчиков on_double_click для разных ALV, нужно укзать имя метода
  • IV_FIRST - Вызвать первым? RAISE EVENT не гарантирует порядок вызова
  • IV_ACTIVATE - Активировать/Деактивировать




А далее остается просто вызвать обработчики событий с помощью CALL_HANDLERS

image

Вместо такой конструкции

    " Handler returns cv_close = 'X' to close SCREEN
    RAISE EVENT zif_eui_manager~pai_event
     EXPORTING
       iv_command = sy-ucomm
       cv_close = REF #( lv_close ).

Вызываем обработчики вот так

    mo_event_caller->call_handlers(
     iv_of_class     = 'ZIF_EUI_MANAGER'
     iv_for_event    = 'PAI_EVENT'
     iv_param_nam_00 = 'SENDER'          iv_param_val_00 = me
     iv_param_nam_01 = 'IV_COMMAND'      iv_param_val_01 = iv_command
     iv_param_nam_02 = 'CV_CLOSE'        iv_param_val_02 = lr_close ).

Делегируем on_double_click вот так

    mo_eui_alv->mo_event_caller->call_handlers(
     iv_of_class     = 'CL_GUI_ALV_GRID'
     iv_for_event    = 'DOUBLE_CLICK'
     iv_param_nam_00 = 'SENDER'          iv_param_val_00 = sender
     iv_param_nam_01 = 'E_ROW'           iv_param_val_01 = e_row
     iv_param_nam_02 = 'E_COLUMN'        iv_param_val_02 = e_column ).

Если вы дочитали до сюда и поняли основную идею, жму вашу руку на расстоянии

В результате устанавливать обработчики (или callback методы), можно через 1 параметр io_handler который имеет описание всех необходимых хэндлеров

Для PAI_EVENT интерфейса ZIF_EUI_MANAGER

 " Handle of PAI_EVENT
 CHECK mo_screen->show( io_handler = me ) = 'OK'.

Для ON_USER_COMMAND класса CL_GUI_ALV_GRID

    " Instead of set handler
    lo_eui_alv->show(
     io_handler        = me

     " If omit map with all (Could be several ON_USER_COMMAND)
     iv_handlers_map   = 'ON_HOTSPOT_CLICK;ON_USER_COMMAND;ON_PBO_EVENT'
    ).

Примеры в тр. SE38

  • ZEUI_TEST_ALV
  • ZEUI_TEST_SCREEN_02