:::: МЕНЮ ::::

Архитектурные шаблоны в ABAP: MVC, MVP, MVVM, MVA

MVC (Model-View-Controller) — фундаментальный шаблон проектирования, целью которого является отделение логики интерфейса пользователя от логики программирования. MVC имеет множество вариантов реализации. Существуют модификации шаблона, самые известные из них MVP и MVVM.
Ниже описана концепция архитектурного шаблона MVC и его модификаций MVP и MVVM. Приведена концепция шаблона MVA, основанная на паттернах MVC, MVP и MVVM и адаптирована под язык ABAP.

Model-View-Controller

  • Model (модель) — содержит модель данных и бизнес-логику. Иными словами, модель делает все для чего создавалась программа.
  • View (представление) — интерфейс взаимодействия с пользователем. Представление отображает часть данных модели пользователю. В некоторых случаях, представление может иметь свою бизнес-логику.
  • Controller (контроллер) — связывает модель и представление между собой. Контроллер обрабатывает события пользователя, управляет состоянием модели.

Схема работы MVC
Точкой входа в программу является контроллер. Контроллер имеет ссылки на модель и на представление. Контроллер подписывается на события представления, а представление подписывается на события модели.
Когда пользователь совершает какие-нибудь действия, управление переходит к контроллеру и контроллер воздействует на модель.
При изменении модели идет воздействие контроллер получает соответствующее событие и воздействует на представление для обновления данных.
В реализации MVC независимой частью является модель, она ничего не знает о представлении и контроллере.
Если программа содержит несколько представлений (например, несколько ABAP-экранов), то для каждого представления требуется создать свой контроллер. При этом, допускается использовать одну и туже модель в разных контроллерах и представлениях.
Реализация MVC в ABAP подробно описана в книге Design Patterns in ABAP Objects с примерами ALV и Web Dynpro представлений.

Model-View-Presenter

Компоненты Model и View в шаблоне MVP аналогичны соответствующим компонентам модели MVC. Основной целью данного шаблона является отделения модели от представления. В шаблоне MVP информация об изменении модели поступает на презентер (Presenter), который воздействует на представление для обновления состояния.
Схема работы MVP
Отсутствие связи между моделью и представлением позволяет сделать абстракцию представления. Реализовать данный паттерн можно путем выделения интерфейса представления IView. Интерфейс будет содержать набор методов и свойств, необходимых презентеру, сам презентер будет подписываться на события представления и вызывать методы для его обновления. Абстрагирование представления полезно в задачах, где требуется отобразить один и тот же набор данных в разных представлениях. Например, выводить отчет в разных форматах: ALV, PDF, EXCEL.

Model-View-ViewModel

Шаблон MVVM предназначен для разработки в WPF на языке C#. Но его идею можно применять и в ABAP. Идея данного паттерна заключается в отделении слов друг от друга. Слой View знает только о ViewModel, ViewModel знает только о Model.
Схема работы MVVM
Представление не требует реализации IView, для получения данных ему требуется передать ссылку на источник данных (DataContext). ViewModel является посредником между передачей данных от модели в DataContext, элементы представления ссылаются на источник данных через биндинги (Binding).
Важное отличие MVVM от MVC и MVP то, что представление в MVVM может менять модель напрямую за счет двухстороннего биндинга. В случае с MVC и MVP представление имеет доступ «только на чтение» к данным модели.

Model-View-Application

Особенностью ABAP приложений является то, то представление может обновиться только после действий пользователя. Даже если какой-нибудь асинхронный процесс поменяет модель, то инициировать обновление представление он не сможет. Данная особенность позволяет ослабить связь модель-представление и делегировать функцию обновления представления самому представлению. Иными словами, представление само должно решать, когда надо обновить себя, а когда нет.
Если взять за основу паттерны MVP и MVVM и принять во внимание отсутствие обратной связи от модели до представления, то можно спроектировать новый архитектурный шаблон MVA.
Схема работы 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.
Структура таблицы ALV
Модель будет реализована в классе ZCL_MVC_DEMO_MODEL.
Для связи модели с представлением будет публичный атрибут.
Конструктор модели будет принимать критерии выбора данных (в данном случае это критерии экрана выбора).
В модели будет публичный метод для инициализации состояния модели.

Представление

Интерфейс представления (IView) будет содержать структуру с типом контекста (Context) и методы для установки контекста и отображения представления. Причем, все компоненты контекста будут ссылками.

Для примера рассмотрим две реализации IView: вывод отчета в формате ALV и в виде списка. Классы ZCL_MVC_DEMO_VIEW_ALV и ZCL_MVC_DEMO_VIEW_LIST соответственно.

Реализация IView в виде списка во многом дублирует код View ALV. Это сделано для простоты понимания.

Приложение (контроллер)

Конструктор приложения принимает на вход критерии приложения (Criteria) и создает экземпляры Model и View, после чего связывает контекст представления с моделью. При этом, критерии приложения состоят из критериев модели и критериев представления. На этом этапе также следует подписывать приложение на события представления. Далее это будет подробно рассмотрено.

Метод RUN приложения инициализирует состояние модели и отображает представление.

Основная программа

Основная программа будет содержать селекционный экран, его PBO/PAI логику и запуск контроллера с передачей ему параметров. Мне встречались реализации, где экран выбора представлен в виде отдельного MVC приложения, но код при этом становился более сложным и громоздким.

Тип структуры Criteria определен в самом классе App, а поля структуры критериев совпадают с параметрами селекционного экрана. В данном примере, параметры F_ALV и F_LIST определяют выбор представления, остальные параметры относятся к критериям модели.
Метод RUN приложения запускает программу. Запуск приложения можно сравнить с запуском транзакции с заранее определенными параметрами экрана. Это позволяет использовать ее из других программ без SUBMIT.

Примечание. Иногда бывает удобнее передать в App не Criteria, а экземпляры Model и View. В таком случае меняются входные параметры конструктора и создание соответствующих объектов выносится за пределы приложения.

Селекционный экран
Демонстрация MVC - экран выбора.

Представление в виде ALV таблицы
Демонстрация MVC - представление ALV.

Представление в виде списка
Демонстрация MVC - представление в виде списка.

Как реализовать сложную логику экрана

Проблемы классов в 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.

В группу функций с представлением создаем GUI статус с кнопкой «Обновить» (REFRESH). В Z_MVC_DEMO_VIEW_ALV_DISPLAY устанавливаем указанный статус и отображаем ALV.
GUI-статус

Расширяем IView, добавляя в него событие REFRESH, которое будет обновлять модель
События в IView

В контроллере делаем обработчик события Refresh и подписываемся на соответствующее событие IView.

Теперь настраиваем обработчик USER_COMMAND для View. При нажатии на кнопку «Обновить» вызываем событие обновления модели данных и обновляем View.

Таким образом View сама себя обновляет. Ни Model ни App никак не воздействуют на View. В ABAP программах View не может обновиться без взаимодействия пользователя. Эту возможность нужно использовать для ослабления связей View.

В обработчике события DOUBLE_CLICK настраиваем проваливание в MM03 при двойном клике на MATNR.

Как можно заметить выше, проваливание в MM03 реализовано в самой View, хоть это можно отнести и к бизнес логике. На уровень View данная логика вынесена исходя из соображений: к модели не требуется обращаться за дополнительными данными; логика относится только к ALV, т.е. если бы была реализация View в виде Excel или PDF, то данное событие невозможно было бы обработать.
Аналогичным способом можно обработать сложную View с подэкранами и полями ввода/вывода. В таком случае PBO и PAI модули будут вызывать соответствующие методы View. При необходимости View будет отправлять события контроллеру.


6 комментариев

  • Ответить Manul74 |

    Очень любопытная статья. Я достаточно долго программирую на ABAP. Но такие паттерны не применял. Да я вывожу базовый select с критериями в класс. Но вот дальше. Зачем на простой вывод отчета городить аж 3 класса? Не понимаю. Ведь можно в базе написать метод show_alv например и он выведет ваш отчет. Написать там же метод print_preview и он выведет на печать. Если чуть сложная задача надо редактировать сохранять добавлять обновлять записи и т д. Делаются методы save update save_as … Как бы можно обойтись 1 классом. Тоесть достаточно только модели. И практически все будет находиться в одном классе.

    • Ответить admin |

      Преимущества данного паттерна можно увидеть разработках со сложной экранной и бизнес-логикой. Простой отчет приведен как пример для понимания принципа паттерна.

  • Ответить Manul74 |

    Да, но в примере совсем простая логика которая в реале не требует 3 класса на простейший отчет.
    Если усложнить задачу. Скажем так. Есть выборка несколько записей. Кликаем по выборке. Открывается диалоговое окно. С каким то полем из этой записи. По нажатию кнопки сохранить, сохраняем запись, обновляем в гриде.
    Было бы неплохо увидеть такую статью.. посложнее.
    Спасибо.

  • Ответить Snowinc |

    Вопрос из разряда «Зачем писать программу на 10 строк, чтобы она вывела Hello World, если можно в блокноте просто написать Hello World». Такой вопрос интересовал меня в школе, на первом уроке по программированию.
    На мой взгляд, даже в простых отчетах имеет смысл использовать MVC (MVA), т.к. всё простое очень часто становится сложным, а переписывать потом никто не хочет и просто наваливает свое «простое» сверху. Типа: «мне надо то всего одну выгрузку в Ексель добавить», «а мне просто добавить редактирование поле», «а мне проверочку»….

  • Ответить gazinurka |

    Автор хорошая статья, особенно для староверов, которые привыкли все делать в подпрограммах. Давно использую MVC и всегда задают один вопрос, зачем городить столько классов на простую программу. Ответ, любая «популярная» программа имеет свойство быстро разрастаться и его сопровождение превращается в ад.

Присоединиться к обсуждению...