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

Abstract Factory

Abstract Factory — породжуючий патерн, що створює сімейство повʼязаних обʼєктів без привʼязки до їхніх конкретних класів. Клієнт отримує фабрику і через неї будує узгоджений набір обʼєктів, які гарантовано сумісні один з одним.

Меблева серія «Сканді»: крісло, диван, стіл — усі в одному стилі. Ти купуєш не окремі меблі, а «сімейство»: замовив в одній фабриці — отримав узгоджений комплект. Інша фабрика видає «Класику»: інше крісло, інший диван, інший стіл — але теж узгоджені між собою.

  • Треба створювати кілька типів продуктів, які мають бути сумісні всередині однієї групи.
  • Треба перемикати цілу групу разом (тема UI: dark/light; БД-діалект: Oracle/HANA; мова документів: UA/EN).
  • Клієнтський код не повинен знати конкретні класи — він працює з інтерфейсами фабрики й інтерфейсами продуктів.

Відмінність від Factory Method

Section titled “Відмінність від Factory Method”
Factory MethodAbstract Factory
Скільки продуктівОдин типКілька типів, повʼязаних між собою
Скільки реалізаційОдна фабрика, багато продуктівБагато фабрик, кожна видає свій набір
ПрикладПарсер JSON чи XMLUI-тема: кнопки + поля + діалоги для dark/light

Приклад: звіти для різних регіонів. Кожен регіон має свої правила форматування дат, валют і заголовків — і всі три мають узгоджуватись.

" Інтерфейси продуктів — що робить кожен компонент
INTERFACE zif_date_formatter.
METHODS format
IMPORTING iv_date TYPE d
RETURNING VALUE(rv_text) TYPE string.
ENDINTERFACE.
INTERFACE zif_currency_formatter.
METHODS format
IMPORTING iv_amount TYPE p
iv_currency TYPE waers
RETURNING VALUE(rv_text) TYPE string.
ENDINTERFACE.
INTERFACE zif_header_builder.
METHODS build
RETURNING VALUE(rv_text) TYPE string.
ENDINTERFACE.
" Абстрактна фабрика — контракт сімейства
INTERFACE zif_locale_factory.
METHODS:
create_date_formatter RETURNING VALUE(ro) TYPE REF TO zif_date_formatter,
create_currency_formatter RETURNING VALUE(ro) TYPE REF TO zif_currency_formatter,
create_header_builder RETURNING VALUE(ro) TYPE REF TO zif_header_builder.
ENDINTERFACE.
" Конкретна фабрика для EU (DD.MM.YYYY, EUR, німецькі заголовки)
CLASS zcl_locale_factory_eu DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_locale_factory.
ENDCLASS.
CLASS zcl_locale_factory_eu IMPLEMENTATION.
METHOD zif_locale_factory~create_date_formatter.
ro = NEW zcl_date_formatter_eu( ).
ENDMETHOD.
METHOD zif_locale_factory~create_currency_formatter.
ro = NEW zcl_currency_formatter_eu( ).
ENDMETHOD.
METHOD zif_locale_factory~create_header_builder.
ro = NEW zcl_header_builder_eu( ).
ENDMETHOD.
ENDCLASS.
" Аналогічно zcl_locale_factory_us, zcl_locale_factory_ua

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

DATA lo_factory TYPE REF TO zif_locale_factory.
CASE p_region.
WHEN 'EU'. lo_factory = NEW zcl_locale_factory_eu( ).
WHEN 'US'. lo_factory = NEW zcl_locale_factory_us( ).
WHEN 'UA'. lo_factory = NEW zcl_locale_factory_ua( ).
ENDCASE.
" Далі код НЕ знає про конкретний регіон:
DATA(lv_date) = lo_factory->create_date_formatter( )->format( sy-datum ).
DATA(lv_amount) = lo_factory->create_currency_formatter( )->format( iv_amount = 1000 iv_currency = 'EUR' ).
DATA(lv_header) = lo_factory->create_header_builder( )->build( ).

Гарантія: усі три виклики повертають обʼєкти одного регіону — не сплутаєш німецький формат дат з доларовою валютою.

  • cl_abap_typedescr + cl_abap_structdescr + cl_abap_tabledescr — сімейство описів типів, отримується через фабрику cl_abap_typedescr=>describe_by_*.
  • Кастомні output-processors (Smart Forms, Adobe Forms, SAP Script) — кандидати на Abstract Factory, коли треба рендерити в усіх трьох форматах за єдиним контрактом.
  • Конвенція: для кожного регіонального customizing-блоку робити окрему фабрику, не IF p_region = 'EU' по всьому коду.
  • Вибух класів. Abstract Factory додає: інтерфейс фабрики + інтерфейси кожного продукту + по одному конкретному класу на (фабрика × продукт). 3 фабрики × 3 продукти = 12+ класів. Застосовуй, коли продуктів дійсно кілька і вони мають бути узгоджені.
  • Додавання нового продукту = правка всіх фабрик. Якщо часто додаєш нові типи продуктів — Abstract Factory стає болючим. Якщо часто додаєш нові сімейства — зручно. Вибирай вісь варіативності.
  • Не ховай вибір фабрики глибоко. Фабрику обирають один раз нагорі (наприклад, у controller->gc_initialization) і пробрасують вниз як DI — щоб тестувати з фейковою фабрикою.

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

Section titled “Коли НЕ використовувати”
  • Продукт один. Бери Factory Method.
  • Продукти не повʼязані між собою. Кілька окремих фабрик краще за одну Abstract Factory, що навʼязує штучну групу.
  • Сімейства продуктів розширюються частіше, ніж самі продукти — Abstract Factory погано масштабується на нові типи продуктів.