Memento
Memento — поведінковий патерн, що дає можливість зберегти і відновити стан обʼєкта без того, щоб зовнішній код лазив у його приватні поля. Три ролі: Originator (обʼєкт зі станом), Memento (знімок), Caretaker (хранитель, що тримає колекцію знімків — зазвичай стек).
Метафора
Section titled “Метафора”Фотоальбом: ти робиш фото себе сьогодні і кладеш в альбом. За рік відкриваєш альбом і «відновлюєш» вигляд — але сам ти не змінюєшся під альбомом, це альбом знає твій минулий стан. Альбом не розуміє, що на фото — він просто зберігає.
Коли застосовувати
Section titled “Коли застосовувати”- Треба undo/redo — стек знімків.
- Треба snapshot стану для rollback після виняткової ситуації.
- Стан обʼєкта складний, а його відновлення з первинних джерел (БД) — дороге або неможливе.
- Треба серіалізувати стан (наприклад, для збереження у Shared Memory або відправки по RFC).
ABAP-реалізація
Section titled “ABAP-реалізація”Ідея: memento — окремий клас або структура, доступна тільки Originator-у. Caretaker тримає memento, але не може читати вміст — тільки передати назад в Originator.
" Memento — PRIVATE-інстанс. GLOBAL FRIENDS дає доступ лише Originator-у.CLASS zcl_editor_memento DEFINITION PUBLIC FINAL CREATE PRIVATE GLOBAL FRIENDS zcl_editor.
PUBLIC SECTION. METHODS constructor IMPORTING is_state TYPE zty_editor_state.
PRIVATE SECTION. DATA ms_state TYPE zty_editor_state.ENDCLASS.
CLASS zcl_editor_memento IMPLEMENTATION. METHOD constructor. ms_state = is_state. ENDMETHOD.ENDCLASS.
" OriginatorCLASS zcl_editor DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS: type IMPORTING iv_text TYPE string, cursor IMPORTING iv_pos TYPE i,
save RETURNING VALUE(ro) TYPE REF TO zcl_editor_memento, restore IMPORTING io_memento TYPE REF TO zcl_editor_memento.
PRIVATE SECTION. DATA ms_state TYPE zty_editor_state.ENDCLASS.
CLASS zcl_editor IMPLEMENTATION. METHOD type. ms_state-text = ms_state-text && iv_text. ENDMETHOD. METHOD cursor. ms_state-cursor_pos = iv_pos. ENDMETHOD.
METHOD save. ro = NEW zcl_editor_memento( is_state = ms_state ). ENDMETHOD.
METHOD restore. " GLOBAL FRIENDS дозволяє читати приватне поле ms_state = io_memento->ms_state. ENDMETHOD.ENDCLASS.
" Caretaker — тримає стек. Не читає і не створює memento сам.CLASS zcl_history DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS: push IMPORTING io_memento TYPE REF TO zcl_editor_memento, pop RETURNING VALUE(ro) TYPE REF TO zcl_editor_memento. PRIVATE SECTION. DATA mt_stack TYPE STANDARD TABLE OF REF TO zcl_editor_memento WITH EMPTY KEY.ENDCLASS.Використання:
DATA(lo_editor) = NEW zcl_editor( ).DATA(lo_history) = NEW zcl_history( ).
lo_editor->type( 'Hello ' ).lo_history->push( lo_editor->save( ) ). " snapshot до другої дії
lo_editor->type( 'world' )." Ой, не те ввівlo_editor->restore( lo_history->pop( ) ). " повертаємосьCaretaker (zcl_history) тримає memento-обʼєкти, але не знає їхнього вмісту — ms_state приватне, доступне тільки через GLOBAL FRIENDS zcl_editor. Інкапсуляція не порушена.
Memento через копію структури (простий варіант)
Section titled “Memento через копію структури (простий варіант)”Якщо стан — структура-значення, і нема потреби ховати її тип від caretaker-а:
METHODS: save RETURNING VALUE(rs_state) TYPE zty_editor_state, restore IMPORTING is_state TYPE zty_editor_state.Швидше і простіше, але caretaker бачить поля стану. Для внутрішнього use — ок.
SAP-специфіка
Section titled “SAP-специфіка”EXPORT TO MEMORY / IMPORT FROM MEMORY— memento на рівні ABAP runtime, не обʼєктний, але концептуально — снапшот.- Shared Memory Objects — memento, що переживає між сесіями.
- RAP Draft — memento для збереження чернетки редагованої сутності.
- Transport Organizer — snapshot змін обʼєктів у пакеті.
Підводні камені
Section titled “Підводні камені”- Shallow vs deep copy — якщо стан містить REF-поля, memento має клонувати їх глибоко, інакше зміна в Originator поламає minented стан. Див. Prototype.
- Навʼязлива дружба.
GLOBAL FRIENDS— крутий інструмент, але створює жорсткий звʼязок двох класів. Перш ніж використовувати — подумай, чи справді memento недоступне ззовні є вимогою. - Stale memento. Memento зроблено, стан обʼєкта змінився, memento вже не відображає актуальний стан — передбачуване, але несподіване для junior-ів. Документуй це в коментарі.
- Undo за межами обʼєкта. Memento відновлює стан обʼєкта. Але якщо обʼєкт вже зробив side-effect (INSERT у БД, email) — undo memento не відкотить його. Див. Command.
Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Стан тривіальний (одне-два поля) — просто збережи змінні.
- Snapshot непотрібен, тільки перевірка «якщо впало — rollback». Використовуй БД-транзакцію/SAP LUW.
- Обʼєкт і так serialize-able через
CALL TRANSFORMATION id— серіалізуй у XML.