Flyweight
Flyweight — структурний патерн для економії памʼяті: замість створювати тисячі однакових обʼєктів, ти ділиш їх через пул. Кожен обʼєкт-flyweight — незмінний (immutable) і може безпечно перевикористовуватись. Розрізняють внутрішній стан (спільний, живе в обʼєкті) і зовнішній (унікальний, передається як параметр).
Метафора
Section titled “Метафора”Видавництво друкує книгу. У тексті — мільйони символів «А», «Б», «В». Для кожного символу у шрифті є один опис (форма, розмір, стиль) — це flyweight. Коли рендеримо текст, ми беремо той самий опис з пулу і просто передаємо координати на сторінці (зовнішній стан). Зберігати опис букви «А» мільйон разів — марнотратство.
Коли застосовувати
Section titled “Коли застосовувати”- Створюється багато ідентичних за змістом обʼєктів.
- Памʼять — обмежений ресурс (великі масиви даних, довго живущі обʼєкти).
- Стан обʼєкта можна розділити на спільну незмінну частину і унікальну контекстуальну.
- Обʼєкти — read-only за суттю (формати, стилі, конфігурації, довідкові дані).
ABAP-реалізація — пул довідкових обʼєктів
Section titled “ABAP-реалізація — пул довідкових обʼєктів”Типова задача: у звіті по замовленнях — тисячі рядків, кожен має «матеріал», і в матеріалу довгий текст. Створювати обʼєкт zcl_material на кожне замовлення — марно, бо матеріалів у системі обмежена кількість.
CLASS zcl_material DEFINITION PUBLIC FINAL CREATE PRIVATE.
PUBLIC SECTION. CLASS-METHODS get IMPORTING iv_matnr TYPE matnr RETURNING VALUE(ro) TYPE REF TO zcl_material RAISING zcx_not_found.
METHODS: get_matnr RETURNING VALUE(rv) TYPE matnr, get_description RETURNING VALUE(rv) TYPE maktx.
PRIVATE SECTION. " пул — спільна CLASS-DATA CLASS-DATA gt_pool TYPE HASHED TABLE OF REF TO zcl_material WITH UNIQUE KEY table_line->mv_matnr.
METHODS constructor IMPORTING iv_matnr TYPE matnr RAISING zcx_not_found.
DATA: mv_matnr TYPE matnr, mv_description TYPE maktx.ENDCLASS.
CLASS zcl_material IMPLEMENTATION.
METHOD get. " cache lookup READ TABLE gt_pool ASSIGNING FIELD-SYMBOL(<lo>) WITH TABLE KEY table_line->mv_matnr COMPONENTS mv_matnr = iv_matnr. IF sy-subrc = 0. ro = <lo>. RETURN. ENDIF.
" cache miss — створюємо і додаємо ro = NEW #( iv_matnr = iv_matnr ). INSERT ro INTO TABLE gt_pool. ENDMETHOD.
METHOD constructor. SELECT SINGLE matnr FROM mara WHERE matnr = @iv_matnr INTO @mv_matnr. IF sy-subrc <> 0. RAISE EXCEPTION NEW zcx_not_found( iv_matnr = iv_matnr ). ENDIF. SELECT SINGLE maktx FROM makt WHERE matnr = @iv_matnr AND spras = @sy-langu INTO @mv_description. ENDMETHOD.
METHOD get_matnr. rv = mv_matnr. ENDMETHOD. METHOD get_description. rv = mv_description. ENDMETHOD.ENDCLASS.Використання:
LOOP AT lt_orders INTO DATA(ls_order). DATA(lo_mat) = zcl_material=>get( ls_order-matnr ). " з пулу " унікальний стан (qty, price) — зовнішній, передається окремо WRITE: / lo_mat->get_description( ), ls_order-qty, ls_order-net_price.ENDLOOP.100 000 замовлень, 500 унікальних матеріалів → у памʼяті 500 обʼєктів замість 100 000.
Внутрішній vs зовнішній стан
Section titled “Внутрішній vs зовнішній стан”Flyweight обовʼязково immutable щодо внутрішнього стану. Якби zcl_material мав setter — зміна через одне посилання вплинула б на всі. Тримай flyweight read-only. Все, що змінюється (quantity, price, delivery_date) — зовнішній стан, живе поза flyweight-ом.
SAP-специфіка
Section titled “SAP-специфіка”- Text pool (
TEXT-001у програмах) — рід flyweight у межах програми. - DDIC data elements + domains — одна визначка типу, тисячі полів у таблицях.
cl_abap_typedescr=>describe_by_name— повертає шерінгові descriptor-обʼєкти, не створює новий на кожен виклик.- Shared Memory Objects — для масштабу між сесіями.
- У проекті — будь-який довідник (matnr, kunnr, lifnr), що читається в циклі, — кандидат на flyweight-кеш через
CLASS-DATA-пул.
Підводні камені
Section titled “Підводні камені”- Не шерь мутабельний стан. Серйозно. Якщо хоч один setter — flyweight вже не flyweight. Роби поля read-only і ініціалізуй тільки в конструкторі.
- Concurrent access. Якщо пул модифікується одночасно з кількох сесій — в ABAP кожна сесія має свій work process, тож
CLASS-DATAsharing між сесіями немає. Для справжньо глобального пулу — Shared Memory Objects. - Розмір пулу. Нескінченний кеш = витік. Визнач стратегію evict (LRU, TTL, ручний
clear). - Equals by identity vs by content. Два flyweight для одного ключа мають бути тим самим обʼєктом (
IS IDENTICAL). Якщо пул помилково створив два — семантика порушена. - Збіг з Singleton / Multiton. Multiton — по суті flyweight. Різниця — у намірі: Multiton — «кілька іменованих singleton-ів», Flyweight — «багато невеликих обʼєктів, які шерим».
Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Обʼєктів небагато — пул не дасть виграшу.
- Обʼєкт мутабельний — шерінг небезпечний.
- Створення обʼєкта дешеве — складний пул додасть більше витрат, ніж виграшу.