Singleton
Singleton — породжуючий патерн, що гарантує: на всю runtime-сесію існує єдиний екземпляр класу, і доступ до нього — через глобальну точку (найчастіше CLASS-METHOD get_instance( )).
Метафора
Section titled “Метафора”Телефонна станція міста — одна. Скільки б абонентів не набирало номер, вони всі проходять через ту саму станцію.
Коли застосовувати
Section titled “Коли застосовувати”- Ресурс, що має існувати в однині: кеш конфігурації, логер, менеджер зʼєднань з зовнішнім сервісом.
- Координація дій через один обʼєкт (черга, лічильник, реєстр).
- Важкий обʼєкт з ліньковим створенням — створити один раз, перевикористовувати.
ABAP-реалізація
Section titled “ABAP-реалізація”Ключові елементи: CREATE PRIVATE (заборона прямого NEW), статичний holder, статичний фабричний метод.
CLASS zcl_config DEFINITION PUBLIC FINAL CREATE PRIVATE.
PUBLIC SECTION. CLASS-METHODS get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_config.
METHODS get_value IMPORTING iv_key TYPE string RETURNING VALUE(rv_value) TYPE string RAISING zcx_config_not_found.
PRIVATE SECTION. CLASS-DATA go_instance TYPE REF TO zcl_config.
METHODS constructor. DATA mt_values TYPE SORTED TABLE OF ty_kv WITH UNIQUE KEY key.ENDCLASS.
CLASS zcl_config IMPLEMENTATION. METHOD get_instance. IF go_instance IS INITIAL. go_instance = NEW #( ). ENDIF. ro_instance = go_instance. ENDMETHOD.
METHOD constructor. " разова ініціалізація: завантаження TVARVC, Customizing, змінних середовища ENDMETHOD.
METHOD get_value. READ TABLE mt_values WITH KEY key = iv_key ASSIGNING FIELD-SYMBOL(<ls>). IF sy-subrc <> 0. RAISE EXCEPTION NEW zcx_config_not_found( iv_key = iv_key ). ENDIF. rv_value = <ls>-value. ENDMETHOD.ENDCLASS.Використання:
DATA(lv_host) = zcl_config=>get_instance( )->get_value( 'API_HOST' ).Thread-safety в ABAP
Section titled “Thread-safety в ABAP”На рівні однієї internal session (один work process, один діалоговий крок) — перевірка IF go_instance IS INITIAL безпечна: work process однопотоковий. Але глобального між work-процесами стану у singleton немає — CLASS-DATA живе у межах internal session. Якщо треба справжньо глобальний кеш — EXPORT TO SHARED MEMORY або SHMA/SHMM-обʼєкти.
SAP-специфіка
Section titled “SAP-специфіка”- Більшість
releasedAPI-фасадів у S/4HANA — по суті singleton-и через статичну фабрику (наприклад,cl_abap_syst=>get_instance( )). cl_badi_manager— singleton-менеджер BAdI.- Для справжньо глобального між-сесійного кешу SAP дає Shared Memory Objects (клас
cl_shm_area, TXSHMA). Це — не singleton у GoF-сенсі, а інша сутність.
Підводні камені
Section titled “Підводні камені”- Прихована залежність.
zcl_config=>get_instance( )всередині методу — це залежність, яку видно тільки у коді. У сигнатурі її нема. Колега прочитаєMETHODS process IMPORTING iv_matnr TYPE matnrі не здогадається, що метод лізе у глобальний конфіг. - Відсутність мока. Якщо
get_instanceповертає конкретний клас — замінити його в тесті можна тільки черезLOCAL FRIENDS+ присвоєнняgo_instanceнапряму. Див. backdoor injection. - Порядок ініціалізації. Якщо конструктор singleton-у лізе в інший singleton — ти привʼязуєшся до порядку створення. Дедлоки рідкі, але бувають.
Альтернативи
Section titled “Альтернативи”| Проблема singleton-у | Що робити замість |
|---|---|
| Глобальний стан ускладнює тести | Constructor injection + передача інстансу як параметра |
| Треба один обʼєкт на запит (не на сесію) | Dependency Injection container — у ABAP цього нема, найближче — фабрика, яку передають як параметр |
| Треба кілька іменованих інстансів | Multiton — див. нижче |
| Треба глобально-глобально (між сесіями) | Shared Memory Objects (cl_shm_area) |
Multiton — варіація
Section titled “Multiton — варіація”Як singleton, але кілька іменованих екземплярів — по одному на ключ:
CLASS zcl_cache DEFINITION PUBLIC FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS get_instance IMPORTING iv_name TYPE string RETURNING VALUE(ro_inst) TYPE REF TO zcl_cache. PRIVATE SECTION. TYPES: BEGIN OF ty_reg, name TYPE string, instance TYPE REF TO zcl_cache, END OF ty_reg. CLASS-DATA gt_registry TYPE HASHED TABLE OF ty_reg WITH UNIQUE KEY name.ENDCLASS.
CLASS zcl_cache IMPLEMENTATION. METHOD get_instance. READ TABLE gt_registry WITH KEY name = iv_name ASSIGNING FIELD-SYMBOL(<ls>). IF sy-subrc <> 0. INSERT VALUE #( name = iv_name instance = NEW #( ) ) INTO TABLE gt_registry ASSIGNING <ls>. ENDIF. ro_inst = <ls>-instance. ENDMETHOD.ENDCLASS.Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Якщо клас вирішує бізнес-задачу — майже ніколи. Зробить тести нестерпними.
- Якщо singleton зʼявився тому, що «лінь передавати через конструктор» — це запах.
- Якщо інстансів насправді треба кілька (наприклад, окремий логер на підсистему) — бери Factory або Multiton.