Adapter
Adapter — структурний патерн: клас-обгортка, що реалізує інтерфейс, який потрібен клієнту, а всередині викликає методи «чужого» класу з несумісним API. Дозволяє інтегрувати legacy-код, бібліотеки без вихідного коду, стандартні SAP-класи — без їх правки.
Метафора
Section titled “Метафора”Перехідник «євровилка → американська розетка». Лампа має євровилку, розетка — американська. Сам переробляти лампу чи міняти розетку — не можна. Встромляєш перехідник — і працює. Перехідник не змінює ні лампу, ні розетку, він перекладає контракт.
Коли застосовувати
Section titled “Коли застосовувати”- Твій код чекає на інтерфейс
X, а є клас з інтерфейсомY— і ти не можеш його змінити. - Треба уніфікувати кілька несумісних API під один контракт.
- Інтегруєшся з legacy-кодом (функціональні модулі, старі Z-класи) — не хочеш поширювати його API по всьому новому коду.
- Треба замінити зовнішню залежність без правки клієнтського коду — через
FRIENDS-конструктор підсунути mock.
ABAP-реалізація
Section titled “ABAP-реалізація”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вне, типізоване.
SAP-специфіка
Section titled “SAP-специфіка”- 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 над системними бібліотеками кодування.
Підводні камені
Section titled “Підводні камені”- 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 до потрібного контракту.
- Інтерфейси сумісні або майже — тривіальна обгортка не заслуговує окремого класу.
- Адаптер стає складніше за адаптований клас — знак, що задача не в перекладі, а в проектуванні.