Visitor
Visitor — поведінковий патерн, що дозволяє додавати нові операції над ієрархією обʼєктів без зміни самих класів. Новий алгоритм — новий клас-відвідувач (visitor). Обʼєкти ієрархії мають метод accept( visitor ), що передає керування назад у visitor через метод, специфічний для конкретного типу (подвійний диспетчер).
Метафора
Section titled “Метафора”Екскурсовод у музеї. Самі експонати не змінюються, але можна прийти з різними гідами: гід-історик розповість одне, гід-інженер — інше, гід-мистецтвознавець — третє. Експонати (receivers) приймають гідів (visitors) і «показують себе» — кожен гід інтерпретує по-своєму.
Коли застосовувати
Section titled “Коли застосовувати”- Є стабільна ієрархія обʼєктів (наприклад, типи документів SAP, типи фінансових транзакцій).
- Потрібно додавати нові операції (експорт у XML, JSON, валідація, підрахунок агрегатів) — частіше, ніж нові типи обʼєктів.
- Розкидати методи по ієрархії — порушує інкапсуляцію або змішує непоєднані обовʼязки.
ABAP-реалізація
Section titled “ABAP-реалізація”" Visitor — знає, як працювати з кожним типомINTERFACE zif_shape_visitor. METHODS: visit_circle IMPORTING io_circle TYPE REF TO zcl_circle, visit_rectangle IMPORTING io_rectangle TYPE REF TO zcl_rectangle, visit_triangle IMPORTING io_triangle TYPE REF TO zcl_triangle.ENDINTERFACE.
" Ієрархія — кожен елемент має accept( )INTERFACE zif_shape. METHODS accept IMPORTING io_visitor TYPE REF TO zif_shape_visitor.ENDINTERFACE.
CLASS zcl_circle DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_shape. METHODS constructor IMPORTING iv_radius TYPE i. DATA mv_radius TYPE i READ-ONLY.ENDCLASS.
CLASS zcl_circle IMPLEMENTATION. METHOD constructor. mv_radius = iv_radius. ENDMETHOD. METHOD zif_shape~accept. io_visitor->visit_circle( me ). " подвійний диспетчер ENDMETHOD.ENDCLASS.
" Конкретний visitor — обчислює площуCLASS zcl_area_visitor DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_shape_visitor. METHODS get_total RETURNING VALUE(rv) TYPE f. PRIVATE SECTION. DATA mv_total TYPE f.ENDCLASS.
CLASS zcl_area_visitor IMPLEMENTATION. METHOD zif_shape_visitor~visit_circle. mv_total = mv_total + '3.14159' * io_circle->mv_radius ** 2. ENDMETHOD. METHOD zif_shape_visitor~visit_rectangle. mv_total = mv_total + io_rectangle->mv_width * io_rectangle->mv_height. ENDMETHOD. METHOD zif_shape_visitor~visit_triangle. mv_total = mv_total + io_triangle->mv_base * io_triangle->mv_height / 2. ENDMETHOD. METHOD get_total. rv = mv_total. ENDMETHOD.ENDCLASS.Використання:
DATA lt_shapes TYPE STANDARD TABLE OF REF TO zif_shape.APPEND NEW zcl_circle( iv_radius = 5 ) TO lt_shapes.APPEND NEW zcl_rectangle( iv_width = 4 iv_height = 3 ) TO lt_shapes.
DATA(lo_area) = NEW zcl_area_visitor( ).LOOP AT lt_shapes INTO DATA(lo_shape). lo_shape->accept( lo_area ).ENDLOOP.
WRITE: / 'Total area:', lo_area->get_total( ).Новий алгоритм — новий клас, наприклад zcl_export_svg_visitor, zcl_validation_visitor. Класи ієрархії (zcl_circle, zcl_rectangle) не змінюються.
Подвійний диспетчер — чому
Section titled “Подвійний диспетчер — чому”В ABAP немає method overloading за runtime-типом. io_visitor->visit( me ) з me : zcl_circle викличе visit_shape, не visit_circle. Щоб диспетчер враховував обидва типи (і visitor, і shape), треба два виклики:
- Клієнт →
shape->accept( visitor )— диспетчер по типу shape. - Всередині
accept→visitor->visit_circle( me )— диспетчер по типу visitor.
Це і є «подвійний диспетчер».
SAP-специфіка
Section titled “SAP-специфіка”- iXML DOM traversal через
if_ixml_node_iterator+ свій visitor — типове застосування для обробки різних типів XML-нод. - AST-подібні обходи (ABAP Test Cockpit, Code Inspector) використовують visitor-like механізм для перевірки різних типів конструкцій.
cl_abap_typedescrієрархія + методиis_a/describe_by_*— імітація visitor без формального інтерфейсу. Коли в коді зʼявляєтьсяCASE type_kind ... WHEN cl_abap_typedescr=>typekind_struct. WHEN typekind_table.— це кандидат на рефакторинг у Visitor.
Підводні камені
Section titled “Підводні камені”- Розподіл даних. Visitor має діставатись до даних обʼєктів ієрархії — або через public getter-и (ламає інкапсуляцію), або через
GLOBAL FRIENDS. Вибирай свідомо. - Вибух методів у visitor-і. Ієрархія з 10 типами = 10 методів у кожному visitor-і. Якщо visitor реалізує лише один — інші методи все одно мусить оголошувати (можна через абстрактний базовий visitor з порожнім дефолтом).
- Складність для новачка. Потік керування у Visitor не очевидний:
accept→visit_*— треба перечитати двічі. Коментар біляaccept( )зберігає час колегам.
Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Ієрархія часто розширюється — Visitor стане тягарем.
- Операцій мало і вони природні для самих обʼєктів (наприклад,
area( )у геометрії — частина обʼєкта, не зовнішня операція). - Немає потреби додавати нові операції ззовні — не ускладнюй.