MVC (Model-View-Controller) — фундаментальный шаблон проектирования, целью которого является отделение логики интерфейса пользователя от логики программирования. MVC имеет множество вариантов реализации. Существуют модификации шаблона, самые известные из них MVP и MVVM.
Ниже описана концепция архитектурного шаблона MVC и его модификаций MVP и MVVM. Приведена концепция шаблона MVA, основанная на паттернах MVC, MVP и MVVM и адаптирована под язык ABAP.
Model-View-Controller
- Model (модель) — содержит модель данных и бизнес-логику. Иными словами, модель делает все для чего создавалась программа.
- View (представление) — интерфейс взаимодействия с пользователем. Представление отображает часть данных модели пользователю. В некоторых случаях, представление может иметь свою бизнес-логику.
- Controller (контроллер) — связывает модель и представление между собой. Контроллер обрабатывает события пользователя, управляет состоянием модели.
Точкой входа в программу является контроллер. Контроллер имеет ссылки на модель и на представление. Контроллер подписывается на события представления, а представление подписывается на события модели.
Когда пользователь совершает какие-нибудь действия, управление переходит к контроллеру и контроллер воздействует на модель.
При изменении модели идет воздействие контроллер получает соответствующее событие и воздействует на представление для обновления данных.
В реализации MVC независимой частью является модель, она ничего не знает о представлении и контроллере.
Если программа содержит несколько представлений (например, несколько ABAP-экранов), то для каждого представления требуется создать свой контроллер. При этом, допускается использовать одну и туже модель в разных контроллерах и представлениях.
Реализация MVC в ABAP подробно описана в книге Design Patterns in ABAP Objects с примерами ALV и Web Dynpro представлений.
Model-View-Presenter
Компоненты Model и View в шаблоне MVP аналогичны соответствующим компонентам модели MVC. Основной целью данного шаблона является отделения модели от представления. В шаблоне MVP информация об изменении модели поступает на презентер (Presenter), который воздействует на представление для обновления состояния.
Отсутствие связи между моделью и представлением позволяет сделать абстракцию представления. Реализовать данный паттерн можно путем выделения интерфейса представления IView. Интерфейс будет содержать набор методов и свойств, необходимых презентеру, сам презентер будет подписываться на события представления и вызывать методы для его обновления. Абстрагирование представления полезно в задачах, где требуется отобразить один и тот же набор данных в разных представлениях. Например, выводить отчет в разных форматах: ALV, PDF, EXCEL.
Model-View-ViewModel
Шаблон MVVM предназначен для разработки в WPF на языке C#. Но его идею можно применять и в ABAP. Идея данного паттерна заключается в отделении слов друг от друга. Слой View знает только о ViewModel, ViewModel знает только о Model.
Представление не требует реализации IView, для получения данных ему требуется передать ссылку на источник данных (DataContext). ViewModel является посредником между передачей данных от модели в DataContext, элементы представления ссылаются на источник данных через биндинги (Binding).
Важное отличие MVVM от MVC и MVP то, что представление в MVVM может менять модель напрямую за счет двухстороннего биндинга. В случае с MVC и MVP представление имеет доступ «только на чтение» к данным модели.
Model-View-Application
Особенностью ABAP приложений является то, то представление может обновиться только после действий пользователя. Даже если какой-нибудь асинхронный процесс поменяет модель, то инициировать обновление представление он не сможет. Данная особенность позволяет ослабить связь модель-представление и делегировать функцию обновления представления самому представлению. Иными словами, представление само должно решать, когда надо обновить себя, а когда нет.
Если взять за основу паттерны MVP и MVVM и принять во внимание отсутствие обратной связи от модели до представления, то можно спроектировать новый архитектурный шаблон MVA.
Концепция MVA
Реализация MVA основана на объектно-ориентированном подходе, где на каждый слой архитектуры будет реализован один или несколько классов. Каждый из слоев обладает рядом свойств.
Представление (View и IView):
- MVA работает с абстракцией представления IView. Все классы View должны содержать реализацию IView.
- IView содержит события, которые требуют взаимодействия с моделью
- IView содержит контекст — ссылка на данные модели, которые необходимо отобразить пользователю
- View может содержать бизнес-логику, которая не требует взаимодействия с моделью. Например, если требуется реализовать из ALV проваливание в карточку контрагента, то данная логика будет относиться к представлению.
Приложение (Application):
- Выполняет роль связки представления и модели и является точкой входа в приложение.
- Имеет критерии запуска — набор параметров, которые определяют с какими параметрами необходимо запустить приложение. Обычно это параметры селекционного экрана.
- Критерии приложения состоят из критериев модели и представления. Например, если на селекционном экране требуется ввести дату проводки и указать флаг вывода отчета PDF или ALV, то дата проводки будет относиться к критериям модели, а флаг PDF и ALV к критериям представления.
- В конструктор приложения передаются критерии запуска. Приложение создает модель и представление, подписывается на события представления, связывает контекст представления с моделью.
Модель (Model):
- Содержит публичные атрибуты, которые необходимы представлению.
- Содержит критерии расчета модели и метод инициализации.
Реализация
В коде я буду придерживаться классических обозначений MVC, за исключением контроллера. Его буду называть Application или App.
В выборе между локальными и глобальными классами предпочтение отдаю последним. Использование глобальных классов позволит отказаться от SUBMIT и CALL TRANSACTION если потребуется вызывать программу из другой разработки.
В качестве примера рассмотрим задачу вывода движений документов материала. Упрощенная версия стандартной транзакции MB51.
Модель
Поля, необходимые для отображения определим в структуре ZSMVC_DEMO_OUTTAB.
Модель будет реализована в классе ZCL_MVC_DEMO_MODEL.
Для связи модели с представлением будет публичный атрибут.
Конструктор модели будет принимать критерии выбора данных (в данном случае это критерии экрана выбора).
В модели будет публичный метод для инициализации состояния модели.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
CLASS zcl_mvc_demo_model DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. TYPES: BEGIN OF mts_criteria, matnr TYPE RANGE OF mseg-matnr, charg TYPE RANGE OF mseg-charg, budat TYPE RANGE OF mkpf-budat, END OF mts_criteria . DATA mt_matmove TYPE STANDARD TABLE OF zsmvc_demo_outtab WITH DEFAULT KEY. METHODS constructor IMPORTING !is_criteria TYPE mts_criteria . METHODS initialization . PROTECTED SECTION. PRIVATE SECTION. DATA ms_criteria TYPE mts_criteria . ENDCLASS. CLASS ZCL_MVC_DEMO_MODEL IMPLEMENTATION. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_MODEL->CONSTRUCTOR * +-------------------------------------------------------------------------------------------------+ * | [--->] IS_CRITERIA TYPE MTS_CRITERIA * +-------------------------------------------------------------------------------------- METHOD constructor. ms_criteria = is_criteria. ENDMETHOD. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_MODEL->INITIALIZATION * +-------------------------------------------------------------------------------------------------+ * +-------------------------------------------------------------------------------------- METHOD initialization. CLEAR mt_matmove[]. SELECT mseg~mblnr mseg~mjahr mseg~zeile mkpf~budat mkpf~blart mseg~matnr mseg~charg mseg~menge mseg~meins mseg~dmbtr mseg~waers INTO CORRESPONDING FIELDS OF TABLE mt_matmove FROM mseg JOIN mkpf ON mkpf~mblnr EQ mseg~mblnr AND mkpf~mjahr EQ mseg~mjahr WHERE mseg~matnr IN ms_criteria-matnr AND mseg~charg IN ms_criteria-charg AND mkpf~budat IN ms_criteria-budat. ENDMETHOD. ENDCLASS. |
Представление
Интерфейс представления (IView) будет содержать структуру с типом контекста (Context) и методы для установки контекста и отображения представления. Причем, все компоненты контекста будут ссылками.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
INTERFACE zif_mvc_demo_view PUBLIC . TYPES: mtt_outtab TYPE STANDARD TABLE OF zsmvc_demo_outtab WITH DEFAULT KEY, BEGIN OF mts_context, outtab TYPE REF TO mtt_outtab, END OF mts_context . DATA ms_context TYPE mts_context . METHODS set_context IMPORTING !is_context TYPE mts_context . METHODS display . ENDINTERFACE. |
Для примера рассмотрим две реализации IView: вывод отчета в формате ALV и в виде списка. Классы ZCL_MVC_DEMO_VIEW_ALV и ZCL_MVC_DEMO_VIEW_LIST соответственно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
CLASS zcl_mvc_demo_view_alv DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_mvc_demo_view . PROTECTED SECTION. PRIVATE SECTION. ALIASES ms_context FOR zif_mvc_demo_view~ms_context . DATA mo_salv TYPE REF TO cl_salv_table . ENDCLASS. CLASS zcl_mvc_demo_view_alv IMPLEMENTATION. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_VIEW_ALV->ZIF_MVC_DEMO_VIEW~DISPLAY * +-------------------------------------------------------------------------------------------------+ * +-------------------------------------------------------------------------------------- METHOD zif_mvc_demo_view~display. DATA lo_functions TYPE REF TO cl_salv_functions_list. DATA lo_columns TYPE REF TO cl_salv_columns_table. IF mo_salv IS NOT BOUND. TRY . CALL METHOD cl_salv_table=>factory IMPORTING r_salv_table = mo_salv CHANGING t_table = ms_context-outtab->*[]. CATCH cx_salv_msg INTO DATA(lx_salv_msg). MESSAGE lx_salv_msg TYPE rs_c_error. ENDTRY. lo_functions = mo_salv->get_functions( ). lo_functions->set_all( abap_true ). lo_columns = mo_salv->get_columns( ). lo_columns->set_optimize( abap_true ). ENDIF. mo_salv->display( ). ENDMETHOD. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_VIEW_ALV->ZIF_MVC_DEMO_VIEW~SET_CONTEXT * +-------------------------------------------------------------------------------------------------+ * | [--->] IS_CONTEXT TYPE MTS_CONTEXT * +-------------------------------------------------------------------------------------- METHOD zif_mvc_demo_view~set_context. IF ms_context IS NOT INITIAL. MESSAGE e674(ilm_stor). " Источник контекста уже установлен, двойное использование невозможно ELSE. ms_context = is_context. ENDIF. ENDMETHOD. ENDCLASS. |
Реализация IView в виде списка во многом дублирует код View ALV. Это сделано для простоты понимания.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
CLASS zcl_mvc_demo_view_list DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_mvc_demo_view . PROTECTED SECTION. PRIVATE SECTION. ALIASES ms_context FOR zif_mvc_demo_view~ms_context . DATA mo_salv TYPE REF TO cl_salv_table . ENDCLASS. CLASS ZCL_MVC_DEMO_VIEW_LIST IMPLEMENTATION. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_VIEW_LIST->ZIF_MVC_DEMO_VIEW~DISPLAY * +-------------------------------------------------------------------------------------------------+ * +-------------------------------------------------------------------------------------- METHOD zif_mvc_demo_view~display. DATA lo_functions TYPE REF TO cl_salv_functions_list. IF mo_salv IS NOT BOUND. TRY . CALL METHOD cl_salv_table=>factory EXPORTING list_display = abap_true IMPORTING r_salv_table = mo_salv CHANGING t_table = ms_context-outtab->*[]. CATCH cx_salv_msg INTO DATA(lx_salv_msg). MESSAGE lx_salv_msg TYPE rs_c_error. ENDTRY. lo_functions = mo_salv->get_functions( ). lo_functions->set_all( abap_true ). ENDIF. mo_salv->display( ). ENDMETHOD. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_VIEW_LIST->ZIF_MVC_DEMO_VIEW~SET_CONTEXT * +-------------------------------------------------------------------------------------------------+ * | [--->] IS_CONTEXT TYPE MTS_CONTEXT * +-------------------------------------------------------------------------------------- METHOD zif_mvc_demo_view~set_context. IF ms_context IS NOT INITIAL. MESSAGE e674(ilm_stor). " Источник контекста уже установлен, двойное использование невозможно ELSE. ms_context = is_context. ENDIF. ENDMETHOD. ENDCLASS. |
Приложение (контроллер)
Конструктор приложения принимает на вход критерии приложения (Criteria) и создает экземпляры Model и View, после чего связывает контекст представления с моделью. При этом, критерии приложения состоят из критериев модели и критериев представления. На этом этапе также следует подписывать приложение на события представления. Далее это будет подробно рассмотрено.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
CLASS zcl_mvc_demo_app DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. TYPES: BEGIN OF mts_criteria. INCLUDE TYPE zcl_mvc_demo_model=>mts_criteria AS model. TYPES: alv TYPE flag, list TYPE flag, END OF mts_criteria . METHODS constructor IMPORTING !is_criteria TYPE mts_criteria . METHODS run . PROTECTED SECTION. PRIVATE SECTION. DATA mo_model TYPE REF TO zcl_mvc_demo_model. DATA mo_view TYPE REF TO zif_mvc_demo_view. ENDCLASS. CLASS ZCL_MVC_DEMO_APP IMPLEMENTATION. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_APP->CONSTRUCTOR * +-------------------------------------------------------------------------------------------------+ * | [--->] IS_CRITERIA TYPE MTS_CRITERIA * +-------------------------------------------------------------------------------------- METHOD constructor. DATA ls_context TYPE zif_mvc_demo_view=>mts_context. mo_model = NEW #( is_criteria-model ). CASE abap_true. WHEN is_criteria-alv. mo_view = NEW zcl_mvc_demo_view_alv( ). WHEN is_criteria-list. mo_view = NEW zcl_mvc_demo_view_list( ). ENDCASE. ls_context-outtab = REF #( mo_model->mt_matmove ). mo_view->set_context( ls_context ). ENDMETHOD. * ---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_MVC_DEMO_APP->RUN * +-------------------------------------------------------------------------------------------------+ * +-------------------------------------------------------------------------------------- METHOD run. mo_model->initialization( ). mo_view->display( ). ENDMETHOD. ENDCLASS. |
Метод RUN приложения инициализирует состояние модели и отображает представление.
Основная программа
Основная программа будет содержать селекционный экран, его PBO/PAI логику и запуск контроллера с передачей ему параметров. Мне встречались реализации, где экран выбора представлен в виде отдельного MVC приложения, но код при этом становился более сложным и громоздким.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
REPORT z_mvc_demo. TABLES: mseg, mkpf. SELECT-OPTIONS s_matnr FOR mseg-matnr. SELECT-OPTIONS s_charg FOR mseg-charg. SELECT-OPTIONS s_budat FOR mkpf-budat. PARAMETERS f_alv TYPE flag RADIOBUTTON GROUP gr0 DEFAULT 'X'. PARAMETERS f_list TYPE flag RADIOBUTTON GROUP gr0. START-OF-SELECTION. PERFORM main. FORM main. DATA ls_criteria TYPE zcl_mvc_demo_app=>mts_criteria. DATA lo_app TYPE REF TO zcl_mvc_demo_app. ls_criteria-matnr = s_matnr[]. ls_criteria-charg = s_charg[]. ls_criteria-budat = s_budat[]. ls_criteria-alv = f_alv. ls_criteria-list = f_list. lo_app = NEW #( ls_criteria ). lo_app->run( ). ENDFORM. |
Тип структуры Criteria определен в самом классе App, а поля структуры критериев совпадают с параметрами селекционного экрана. В данном примере, параметры F_ALV и F_LIST определяют выбор представления, остальные параметры относятся к критериям модели.
Метод RUN приложения запускает программу. Запуск приложения можно сравнить с запуском транзакции с заранее определенными параметрами экрана. Это позволяет использовать ее из других программ без SUBMIT.
Примечание. Иногда бывает удобнее передать в App не Criteria, а экземпляры Model и View. В таком случае меняются входные параметры конструктора и создание соответствующих объектов выносится за пределы приложения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
FORM main. DATA ls_criteria TYPE z_mvc_demo_model=>mts_criteria. DATA lo_model TYPE REF TO zcl_mvc_demo_model. DATA lo_view TYPE REF TO zif_mvc_demo_view. DATA lo_app TYPE REF TO zcl_mvc_demo_app. ls_criteria-matnr = s_matnr[]. ls_criteria-charg = s_charg[]. ls_criteria-budat = s_budat[]. lo_model = NEW #( ls_criteria ). CASE abap_true. WHEN f_alv. lo_view = NEW zcl_mvc_demo_view_alv( ). WHEN f_list. lo_view = NEW zcl_mvc_demo_view_list( ). ENDCASE. lo_app = NEW #( io_model = lo_model io_view = lo_view ). lo_app->run( ). ENDFORM. |
Селекционный экран
Представление в виде ALV таблицы
Представление в виде списка
Как реализовать сложную логику экрана
Проблемы классов в ABAP в том, что на них нельзя построить полноценный GUI. Ситуации, в которых стандартного CL_SALV_TABLE достаточно довольно редки. Поэтому, для определения GUI-заголовков/статусов и экранов следует создавать группу функций. ГФ будет тесно связана с View. По сути, она будет являться частью View.
Всю PBO и PAI логику должен обрабатывать класс View. При необходимости взаимодействия с Model, View должно вызывать событие, которое будет обрабатывать контроллер.
Расширение View
Допустим нам потребовалось расширить программу: добавить кнопку с обновлением данных, реализовать «проваливание» в MIGO по документу материала и в MM03 по коду ОЗМ.
Дальнейшие действия будем проводить только с ALV представлением, но на практике нужно будет адаптировать все реализации IView.
Для реализации данной задачу GUI логику перенесем в функциональный модуль Z_MVC_DEMO_VIEW_ALV_DISPLAY, который будет принимать ссылку на CL_SALV_TABLE.
В классе ZCL_MVC_DEMO_VIEW_ALV объявим обработчики событий SALV: ON_USER_COMMAND, ON_DOUBLE_CLICK. Метод DISPLAY будет создавать экземпляр CL_SALV_TABLE и подписываться на события. Управление по выводу GUI будет передаваться в Z_MVC_DEMO_VIEW_ALV_DISPLAY.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
METHOD zif_mvc_demo_view~display. DATA lo_event TYPE REF TO cl_salv_events_table. IF mo_salv IS NOT BOUND. TRY . CALL METHOD cl_salv_table=>factory IMPORTING r_salv_table = mo_salv CHANGING t_table = ms_context-outtab->*[]. CATCH cx_salv_msg INTO DATA(lx_salv_msg). MESSAGE lx_salv_msg TYPE rs_c_error. ENDTRY. lo_event = mo_salv->get_event( ). SET HANDLER on_user_command FOR lo_event. SET HANDLER on_double_click FOR lo_event. ENDIF. CALL FUNCTION 'Z_MVC_DEMO_VIEW_ALV_DISPLAY' EXPORTING io_salv = mo_salv. ENDMETHOD. |
В группу функций с представлением создаем GUI статус с кнопкой «Обновить» (REFRESH). В Z_MVC_DEMO_VIEW_ALV_DISPLAY устанавливаем указанный статус и отображаем ALV.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
FUNCTION z_mvc_demo_view_alv_display. *"---------------------------------------------------------------------- *"*"Локальный интерфейс: *" IMPORTING *" REFERENCE(IO_SALV) TYPE REF TO CL_SALV_TABLE *"---------------------------------------------------------------------- CONSTANTS lc_pf_status TYPE sypfkey VALUE 'MVC_DEMO_GUI_STATUS'. DATA lo_columns TYPE REF TO cl_salv_columns_table. IF abap_true IS INITIAL. SET PF-STATUS lc_pf_status. " Фиктивный вызов для журнала использования ELSE. io_salv->set_screen_status( report = sy-repid pfstatus = lc_pf_status set_functions = cl_salv_table=>c_functions_all ). ENDIF. lo_columns = io_salv->get_columns( ). lo_columns->set_optimize( abap_true ). io_salv->display( ). ENDFUNCTION. |
Расширяем IView, добавляя в него событие REFRESH, которое будет обновлять модель
В контроллере делаем обработчик события Refresh и подписываемся на соответствующее событие IView.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
CLASS ZCL_MVC_DEMO_APP IMPLEMENTATION. … METHOD constructor. DATA ls_context TYPE zif_mvc_demo_view=>mts_context. mo_model = NEW #( is_criteria-model ). CASE abap_true. WHEN is_criteria-alv. mo_view = NEW zcl_mvc_demo_view_alv( ). WHEN is_criteria-list. mo_view = NEW zcl_mvc_demo_view_list( ). ENDCASE. SET HANDLER refresh FOR mo_view. ls_context-outtab = REF #( mo_model->mt_matmove ). mo_view->set_context( ls_context ). ENDMETHOD. METHOD refresh. mo_model->refresh( ). ENDMETHOD. … ENDCLASS. |
Теперь настраиваем обработчик USER_COMMAND для View. При нажатии на кнопку «Обновить» вызываем событие обновления модели данных и обновляем View.
1 2 3 4 5 6 7 8 |
METHOD on_user_command. CASE e_salv_function. WHEN 'REFRESH'. RAISE EVENT refresh. mo_salv->refresh( ). WHEN OTHERS. ENDCASE. ENDMETHOD. |
Таким образом View сама себя обновляет. Ни Model ни App никак не воздействуют на View. В ABAP программах View не может обновиться без взаимодействия пользователя. Эту возможность нужно использовать для ослабления связей View.
В обработчике события DOUBLE_CLICK настраиваем проваливание в MM03 при двойном клике на MATNR.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
METHOD on_double_click. FIELD-SYMBOLS <ls> LIKE LINE OF ms_context-outtab->*[]. READ TABLE ms_context-outtab->*[] ASSIGNING <ls> INDEX row. CHECK sy-subrc EQ 0. CASE column. WHEN 'MATNR'. SET PARAMETER ID 'MAT' FIELD <ls>-matnr. CALL TRANSACTION 'MM03' AND SKIP FIRST SCREEN. WHEN OTHERS. ENDCASE. ENDMETHOD. |
Как можно заметить выше, проваливание в MM03 реализовано в самой View, хоть это можно отнести и к бизнес логике. На уровень View данная логика вынесена исходя из соображений: к модели не требуется обращаться за дополнительными данными; логика относится только к ALV, т.е. если бы была реализация View в виде Excel или PDF, то данное событие невозможно было бы обработать.
Аналогичным способом можно обработать сложную View с подэкранами и полями ввода/вывода. В таком случае PBO и PAI модули будут вызывать соответствующие методы View. При необходимости View будет отправлять события контроллеру.
Спасибо за информацию буду знать
Очень любопытная статья. Я достаточно долго программирую на ABAP. Но такие паттерны не применял. Да я вывожу базовый select с критериями в класс. Но вот дальше. Зачем на простой вывод отчета городить аж 3 класса? Не понимаю. Ведь можно в базе написать метод show_alv например и он выведет ваш отчет. Написать там же метод print_preview и он выведет на печать. Если чуть сложная задача надо редактировать сохранять добавлять обновлять записи и т д. Делаются методы save update save_as … Как бы можно обойтись 1 классом. Тоесть достаточно только модели. И практически все будет находиться в одном классе.
Преимущества данного паттерна можно увидеть разработках со сложной экранной и бизнес-логикой. Простой отчет приведен как пример для понимания принципа паттерна.
Да, но в примере совсем простая логика которая в реале не требует 3 класса на простейший отчет.
Если усложнить задачу. Скажем так. Есть выборка несколько записей. Кликаем по выборке. Открывается диалоговое окно. С каким то полем из этой записи. По нажатию кнопки сохранить, сохраняем запись, обновляем в гриде.
Было бы неплохо увидеть такую статью.. посложнее.
Спасибо.
Вопрос из разряда «Зачем писать программу на 10 строк, чтобы она вывела Hello World, если можно в блокноте просто написать Hello World». Такой вопрос интересовал меня в школе, на первом уроке по программированию.
На мой взгляд, даже в простых отчетах имеет смысл использовать MVC (MVA), т.к. всё простое очень часто становится сложным, а переписывать потом никто не хочет и просто наваливает свое «простое» сверху. Типа: «мне надо то всего одну выгрузку в Ексель добавить», «а мне просто добавить редактирование поле», «а мне проверочку»….
Автор хорошая статья, особенно для староверов, которые привыкли все делать в подпрограммах. Давно использую MVC и всегда задают один вопрос, зачем городить столько классов на простую программу. Ответ, любая «популярная» программа имеет свойство быстро разрастаться и его сопровождение превращается в ад.