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

Flyweight

Flyweight — структурний патерн для економії памʼяті: замість створювати тисячі однакових обʼєктів, ти ділиш їх через пул. Кожен обʼєкт-flyweight — незмінний (immutable) і може безпечно перевикористовуватись. Розрізняють внутрішній стан (спільний, живе в обʼєкті) і зовнішній (унікальний, передається як параметр).

Видавництво друкує книгу. У тексті — мільйони символів «А», «Б», «В». Для кожного символу у шрифті є один опис (форма, розмір, стиль) — це flyweight. Коли рендеримо текст, ми беремо той самий опис з пулу і просто передаємо координати на сторінці (зовнішній стан). Зберігати опис букви «А» мільйон разів — марнотратство.

  • Створюється багато ідентичних за змістом обʼєктів.
  • Памʼять — обмежений ресурс (великі масиви даних, довго живущі обʼєкти).
  • Стан обʼєкта можна розділити на спільну незмінну частину і унікальну контекстуальну.
  • Обʼєкти — 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-ом.

  • 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-пул.
  • Не шерь мутабельний стан. Серйозно. Якщо хоч один setter — flyweight вже не flyweight. Роби поля read-only і ініціалізуй тільки в конструкторі.
  • Concurrent access. Якщо пул модифікується одночасно з кількох сесій — в ABAP кожна сесія має свій work process, тож CLASS-DATA sharing між сесіями немає. Для справжньо глобального пулу — 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 “Коли НЕ використовувати”
  • Обʼєктів небагато — пул не дасть виграшу.
  • Обʼєкт мутабельний — шерінг небезпечний.
  • Створення обʼєкта дешеве — складний пул додасть більше витрат, ніж виграшу.