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

Adapter

Adapter — структурний патерн: клас-обгортка, що реалізує інтерфейс, який потрібен клієнту, а всередині викликає методи «чужого» класу з несумісним API. Дозволяє інтегрувати legacy-код, бібліотеки без вихідного коду, стандартні SAP-класи — без їх правки.

Перехідник «євровилка → американська розетка». Лампа має євровилку, розетка — американська. Сам переробляти лампу чи міняти розетку — не можна. Встромляєш перехідник — і працює. Перехідник не змінює ні лампу, ні розетку, він перекладає контракт.

  • Твій код чекає на інтерфейс X, а є клас з інтерфейсом Y — і ти не можеш його змінити.
  • Треба уніфікувати кілька несумісних API під один контракт.
  • Інтегруєшся з legacy-кодом (функціональні модулі, старі Z-класи) — не хочеш поширювати його API по всьому новому коду.
  • Треба замінити зовнішню залежність без правки клієнтського коду — через FRIENDS-конструктор підсунути mock.

Object Adapter — через композицію (рекомендовано)

Section titled “Object Adapter — через композицію (рекомендовано)”

Adapter тримає посилання на адаптований обʼєкт і делегує.

" Контракт, якого хоче клієнт
INTERFACE zif_logger.
METHODS log
IMPORTING iv_message TYPE string
iv_severity TYPE symsgty.
ENDINTERFACE.
" Legacy-клас, який ми НЕ можемо змінити
CLASS zcl_legacy_logger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS write_log
IMPORTING iv_text TYPE string
iv_type TYPE c. " 'E', 'W', 'I'
ENDCLASS.
" Адаптер
CLASS zcl_legacy_logger_adapter DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_logger.
METHODS constructor
IMPORTING io_legacy TYPE REF TO zcl_legacy_logger.
PRIVATE SECTION.
DATA mo_legacy TYPE REF TO zcl_legacy_logger.
ENDCLASS.
CLASS zcl_legacy_logger_adapter IMPLEMENTATION.
METHOD constructor.
mo_legacy = io_legacy.
ENDMETHOD.
METHOD zif_logger~log.
" переклад нового контракту у старий
mo_legacy->write_log(
iv_text = iv_message
iv_type = CONV #( iv_severity ) ).
ENDMETHOD.
ENDCLASS.

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

DATA(lo_logger) = CAST zif_logger(
NEW zcl_legacy_logger_adapter( NEW zcl_legacy_logger( ) ) ).
lo_logger->log( iv_message = 'something happened' iv_severity = 'W' ).

Новий код бачить тільки zif_logger. Legacy-клас заховано.

Class Adapter — через наслідування (рідше)

Section titled “Class Adapter — через наслідування (рідше)”

Коли адаптер може одночасно успадкувати адаптований клас і реалізувати інтерфейс:

CLASS zcl_legacy_logger_adapter DEFINITION PUBLIC FINAL
INHERITING FROM zcl_legacy_logger.
PUBLIC SECTION.
INTERFACES zif_logger.
ENDCLASS.
CLASS zcl_legacy_logger_adapter IMPLEMENTATION.
METHOD zif_logger~log.
write_log( iv_text = iv_message iv_type = CONV #( iv_severity ) ).
ENDMETHOD.
ENDCLASS.

Менше коду, але жорсткіша звʼязка. Для SAP — часто не підходить: багато стандартних класів FINAL, успадкування неможливе.

Адаптер для функціонального модуля

Section titled “Адаптер для функціонального модуля”

Типова задача у ABAP — обгорнути FM в обʼєкт:

CLASS zcl_bapi_so_reader_adapter DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_so_reader.
ENDCLASS.
CLASS zcl_bapi_so_reader_adapter IMPLEMENTATION.
METHOD zif_so_reader~read.
CALL FUNCTION 'BAPI_SALESORDER_GETDETAILBOS'
EXPORTING salesdocument = iv_id
TABLES order_header_in = lt_header
order_header_out = lt_header_out
EXCEPTIONS OTHERS = 1.
IF sy-subrc <> 0.
RAISE EXCEPTION NEW zcx_so_not_found( iv_id = iv_id ).
ENDIF.
" переклад BAPI-структур у нашу DTO
rs_order = CORRESPONDING #( lt_header_out[ 1 ] ).
ENDMETHOD.
ENDCLASS.

Добра практика: BAPI/FM не викликаються з бізнес-логіки напряму, а тільки через adapter. Тестоване, mock-oвне, типізоване.

  • SALV wrapper — частковий adapter над cl_salv_table: зводить множину способів налаштування до одного fluent-інтерфейсу.
  • DAO-класи у abap-specific/dao — adapter-и над БД, що переводять SQL-запити в обʼєктний API.
  • BAdI class implementation — іноді adapter, якщо SAP-стандарт викликає BAdI з одним контрактом, а replacement хоче інший.
  • cl_abap_codepage — adapter над системними бібліотеками кодування.
  • Leaky adapter. Якщо в adapter-і «просочуються» типи legacy (наприклад, BAPI-структури напряму у public-методах) — весь сенс adapter-а втрачається. Переконайся, що public API чистий.
  • Втрата інформації при перекладі. Нова модель може бути бідніша, ніж legacy — деталі губляться. Або, навпаки, нова модель хоче поля, яких у legacy нема. Документуй mapping.
  • Exceptions. Legacy часто повертає sy-subrc, адаптер має перекласти у виняток-клас, але не забудь додати context (параметри виклику) — інакше дебажити потім нереально.
  • Thin vs fat adapter. Тонкий adapter — лише переклад. Товстий — починає робити бізнес-логіку. Тримай тонким; логіка — вище по стеку.

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

Section titled “Коли НЕ використовувати”
  • Ти контролюєш legacy-код — просто приведи його API до потрібного контракту.
  • Інтерфейси сумісні або майже — тривіальна обгортка не заслуговує окремого класу.
  • Адаптер стає складніше за адаптований клас — знак, що задача не в перекладі, а в проектуванні.