[Из песочницы] Предметно-ориентированные языки для бизнес-приложений — пользовательские интерфейсы

Habrahabr 1

Эта статья 一 перевод статьи Domain-Specific Languages for Business Applications — Modelling User Interfaces

Разработка бизнес-приложений связана с решением задач в различных предметных областях, таких как хранение данных, обработка бизнес-логики, проектирование интерфейса пользователя. Для прироста производительности и качества не хватит ни одного предметно-ориентированного языка (domain-specific language, DSL), ни нескольких, если они недостаточно интегрированы между собой. Значительные улучшения может принести лишь целостный подход, при котором согласованно применяются несколько DSL для моделирования решений в различных предметных областях.

В этой статье мы рассмотрим пример DSL, с помощью которого мы моделируем стандартизованные пользовательские интерфейсы для наших бизнес-приложений в modellwerkstatt.org. Логика, присущая DSL, позволяет визуализировать графы объектов полностью и в очень простом, декларативном виде. Мы покажем, насколько просто внедряется обычный код Java, с которым можно взаимодействовать, что обеспечивает дополнительную гибкость и безопасность, в частности типобезопасность. Указав на различие между внутренними и внешними DSL, мы перейдем к JetBrains MPS и сразу же рассмотрим наш DSL для интерфейсов пользователя. Наконец, мы приведем ряд общих соображений относительно взаимодействия DSL между собой и их расширения.

Внутренние и внешние DSL

Предметно-ориентированные языки широко применяются в современной разработке программных приложений. Им можно дать следующее определение: «языки программирования, выводящие уровень абстракции за рамки программирования, задавая решение, в котором непосредственно используются концепции и правила из конкретной предметной области». (Kelly и Tolvanen, 2008). Хотя языки общего назначения (General Purpose Language, GPL), такие как Java или Kotlin, широко применяются в самых разных предметных областях, предметно-ориентированные языки адаптированы к конкретной предметной области, что обеспечивает им чрезвычайно высокую эффективность в этой области. Такие языки предоставляют абстракции, адекватные решаемой проблеме, и позволяют выражать решения кратко и емко, причем для использования таких решений зачастую не требуется квалификация программиста.

В качестве примера DSL часто приводят язык запросов SQL. SQL позволяет пользователям обрабатывать данные, таблицы и базы данных, не обладая какими-либо навыками программирования. SQL обеспечивает высокий уровень абстракции благодаря конкретным концепциям, таким как «выборка» или «обновление», в предметной области «данные в строках и столбцах». Язык SQL четок и выразителен, но при этом он приковывает пользователя к одной конкретной предметной области. Небезызвестны также такие DSL, как CSS, регулярные выражения и XPath. Помимо этих «всеобщих» DSL создано и множество узкоспециальных, применяемых в отдельных организациях для фиксации предметных знаний и автоматизированной генерации однообразного кода на языках общего назначения.

Широко распространены так называемые внутренние DSL, опирающиеся на базовый GPL. «Внутренний DSL— это особая форма API в базовом языке общего назначения, часто также называемая текучим интерфейсом» (Fowler, 2010). Внутренний DSL определяет концепции и операции, существующие лишь в конкретной предметной области, и помогает работать именно в этой области. Хорошо известны примеры внутренних DSL, построенных на Java — JMock и JooQ. Однако внутренним DSL свойственны существенные ограничения. Синтаксис внутреннего DSL не может нарушать синтаксис базового языка общего назначения. В противном случае компилятор базового языка не смог бы проводить синтаксический анализ полученного кода и компилировать его. Кроме того, поддержка подсветки синтаксиса, автодополнения и контроля типов в средах IDE также ограничена для DSL и зависит от базового GPL. Из этого следует, что, во-первых, внутренние DSL могут оказаться полезными лишь разработчикам, владеющим GPL, а во-вторых, на синтаксис и визуальное представление накладываются суровые ограничения.

SQL же можно считать внешним DSL, поскольку он не встраивается в какой-либо другой GPL и не базируется на нем. SQL предоставляет собственные ключевые слова, операции и синтаксис. Он был задуман вне какой-либо (синтаксической) связи с языками общего назначения — он совершенно независим. Внешние DSL поддаются оптимизации для обеспечения полноты охвата информации в предметной области и максимальной выразительности без каких-либо ограничений по читаемости и понятности. Визуальное представление, эстетика и эргономика внешнего DSL могут быть разработаны с учетом требований не только разработчиков, но, возможно, и собственно специалистов по предметной области. Только в этом случае возможны улучшения в части формулирования, коммуницирования и понимания решений в определенной предметной области. Именно эти соображения изначально и подтолкнули к созданию предметно-ориентированных языков.

Тем не менее, внешние DSL обладают и рядом недостатков. Во-первых, поскольку такой язык не базируется на языке общего назначения, для него изначально отсутствует среда IDE. Это особенно верно в случаях, когда DSL нацелен на крайне узкую предметную область, т. е. если потенциальное количество пользователей языка не оправдывает трудозатраты на создание языкового инструментария или полноценной среды IDE. При этом пользователям DSL все же необходимы интеллектуальные инструменты для помощи в работе и повышения производительности труда. Во-вторых, что более важно, внешние DSL не предусматривают простого способа обращения к внешним артефактам, например к коду на языке общего назначения или декларациям, хранимым в виде XML. Внешний DSL в некотором смысле остается изолированным, пока не будут дополнительно разработаны средства импорта и экспорта (генераторы, производящие исполняемый код на языке общего назначения). На практике взаимодействие с полной средой разработки чрезвычайно важно, поскольку один лишь DSL не позволяет охватить все аспекты программного решения. Именно здесь могут помочь средства разработки языков.

JetBrains MPS

JetBrains MPS — это средство разработки языков, которое позволяет определять, многократно использовать и составлять предметно-ориентированные языки и предоставляет дополнительный инструментарий для этих языков.

В отличие от других сред разработки, MPS предоставляет широчайшую поддержку инструментов, не опираясь при этом на синтаксические анализаторы. Обычно инструменты и компиляторы получают абстрактное синтаксическое дерево (АСД), подвергая синтаксическому анализу текстовое представление кода с достаточно определенной грамматикой. В дальнейшем с использованием этого структурированного дерева переданная информация преобразуется в исполняемый код.

В отличие от этого подхода, в MPS манипулирование АСД выполняется непосредственно, с помощью проекционного редактора. Хотя проекционный редактор и похож на текстовый редактор, пользователь в нем редактирует структуру дерева в режиме реального времени. Для хранения кода не используется текст — сохраняется само АСД, что устраняет необходимость синтаксического анализа. Следовательно, исключается синтаксическая двусмысленность, а для разграничения концепций не требуются точки с запятой или скобки. Проекционный редактор обеспечивает краткость и адекватность визуального представления предметной области. Автодополнение кода и контроль ошибок легко реализуются через исследование и обход АСД — все это поддерживается в IDE MPS без лишних ухищрений. Короче говоря, MPS обеспечивает инструментальную поддержку предметно-ориентированных языков в общем виде, избавляя от необходимости реализовывать ее отдельно для каждого языка.

Использование АСД в качестве основы MPS дает важное преимущество. Поскольку устраняется двусмысленность грамматики, языки можно комбинировать любыми способами. Существующие языки можно расширять и обогащать новыми концепциями. Языки могут дополнять друг друга, наследовать функционал и возможности. Это позволяет без труда многократно использовать существующие языковые концепции, что ускоряет разработку и способствует единому подходу.

Чтобы проиллюстрировать эти соображения с практической точки зрения, обратимся к нашему примеру.

DataUx — язык пользовательского интерфейса, основанный на Java

DataUx удачно демонстрирует идею комбинирования языков. Мы создали этот язык специально для спецификаций пользовательских интерфейсов Java-приложений, предназначенных для интенсивной обработки данных, — обычно это бизнес-приложения, например для управления счетами на оплату или для контроля складских запасов. DataUx предоставляет различные языковые концепции, позволяющие задавать формы ввода данных, таблицы для визуализации коллекций и макеты. В DataUx элементы интерфейса пользователя снабжаются фактическими значениями данных посредством расширенной привязки свойств. Объекты Java со свойствами компонента (получатели и установщики для определенных идентификаторов) организуются в граф объектов. Затем — и это, на наш взгляд, очень необычно — весь этот граф привязывается к интерфейсу пользователя. В рассматриваемом простом примере объект заказа (Order) с различными свойствами также содержит список позиций заказа (OrderPosition). На следующем снимке экрана показан пользовательский интерфейс для объекта Order с его свойствами и позициями заказа. На втором снимке экрана показана спецификация этого самого пользовательского интерфейса средствами DataUx.

ORDER Demo App

Снимок экрана 1: пользовательский интерфейс демонстрационного приложения с одним объектом Order, содержащим несколько объектов OrderPosition

ORDER DSL

Снимок экрана 2: спецификация пользовательского интерфейса Order для демонстрационного приложения на языке DataUx в MPS

Выше на снимке экрана 2 показана «главная страница заказа», привязанная к списку объектов Order. В макете сетки, состоящем из единственного столбца (1* = максимизация с весовым коэффициентом 1), мы разместили два элемента пользовательского интерфейса в две строки, первая из них минимизируется, насколько возможно (-1), а вторая опять максимизируется (1*). На форме «Delegate Form» значения шести свойств объекта Order показываются в двух столбцах; в таблице отображается список позиций данного заказа. Ключевое слово DISABLED переводит всю форму в режим только для чтения; таблица и без дополнительной настройки доступна только для чтения по умолчанию.

Главная идея языка DataUx основана на привязке компонентов и на шаблоне «основные данные – подробные данные». Каждая панель PagePane способна визуализировать граф, состоящий из объектов, в котором для каждого типа объектов существует «выбранный объект». В нашем примере есть объекты двух типов: Order и OrderPosition. Таким образом, существенными являются два текущих выбора: выбранный заказ и выбранная позиция заказа. Чтобы в элемент пользовательского интерфейса загружался текущий выбранный объект определенного типа, каждый элемент интерфейса должен быть привязан к соответствующему типу объекта. В отдельных элементах пользовательского интерфейса можно обращаться к свойствам объектов и привязывать их.

Форма «Delegate Form» привязана к заказу, выбранному в текущий момент, а таблица привязана к списку позиций выбранного заказа (записывается как Order.pos). Следовательно, форма «Delegate Form» может визуализировать свойства объекта Order, в то время как таблица может визуализировать свойства объектов OrderPosition в своих столбцах. Для OrderPosition объявлены пять свойств и задана ширина соответствующих столбцов.

В рамках нашей логики «выбранного объекта» таблица используется не только для отображения списков объектов, но и для определения выбранного объекта. Выбранная строка таблицы напрямую соответствует «выбранному объекту» данного типа. В нашем примере при выборе другой строки в таблице происходит выбор и определение объекта типа OrderPosition. Таким образом, пользовательский интерфейс удалось улучшить за счет визуализации подробных данных позиции заказа (ее свойств) в другой форме, привязанной непосредственно к объекту OrderPosition.

«Главная страница заказа» со второго снимка экрана по типу является списком объектов Order, однако если передан только один заказ, он автоматически становится выбранным объектом данного типа. Это особенность нашей концепции PagePane. Если бы странице было передано несколько заказов, для определения выбранного заказа потребовалась бы дополнительная таблица. Только это позволило бы реализовать выбранный заказ, что необходимо для формы подробных данных и таблицы OrderPosition. На третьем снимке экрана показан более сложный случай, предусматривающий несколько заказов. Как уже было сказано, для отображения свойств выбранной позиции заказа (объекта OrderPosition) мы добавили форму подробных данных.

ORDER Page

Снимок экрана 3: более сложный пользовательский интерфейс для демонстрационного приложения с отношениями «основные данные – подробные данные»

Продолжая следовать этой логике привязки объектов, с помощью DataUx нетрудно задать пользовательские интерфейсы для сложных графов объектов. Пользователь привязывает элементы пользовательского интерфейса к типу объекта, чтобы обращаться к определенным свойствам данного объекта. Во время исполнения в элемент пользовательского интерфейса загружается текущий выбранный объект соответствующего типа. Это, вероятно, самый сложный для понимания момент в языке DataUx. Однако такая логика привязки объектов проверяется системой типов DataUx и еще целым рядом проверок на непротиворечивость. Для интеллектуального редактирования предусмотрено автодополнение, чувствительное к контексту, и даже «автодополнение элементов пользовательского интерфейса».

В контексте обсуждения предметно-ориентированных языков примечательно отношение между DataUx и Java. Очевидно, что хотя DataUx не совместим с синтаксисом Java, он опирается на такие концепции Java, как свойства и классы. Присутствуют даже правила проверки типов, основанные на Java. Более того, среди элементов пользовательского интерфейса упоминаются внешние артефакты Java. Как это достигается?

JetBrains MPS поставляется с полнофункциональной реализацией Java. Реализация этого GPL в MPS ничем не отличается от реализации любого специализированного DSL. Имеется неплохой проекционный редактор, воспроизводящий привычную модель работы с интеллектуальным текстовым редактором Java — поддерживаются определения классов и интерфейсов, их членов, и конечно, выражений. Поскольку в MPS также есть средство импорта jar, им можно пользоваться как автономной полнофункциональной IDE Java. Мы расширили и дополнили эту реализацию Java языком DataUx. В результате появилась возможность использовать мощные концепции Java, в особенности выражения и типы. Таким образом, DataUx получил базовую поддержку программирования без каких-либо дополнительных усилий.

Задавая элементы пользовательского интерфейса на языке DataUx, пользователи могут ссылаться на объявленные объекты Java и их свойства, если они загружены в среду IDE и доступны в ней. Интеллектуальные области доступа для ссылок на классы, свойств и выбора вычисляются во время редактирования. Как можно видеть на снимке экрана 2, параметр DataUx «Table Summary Line» предоставляет концепцию, аналогичную замыканиям, с переменной «allObjects» (тип — list). В правой части ожидается выражение Java, возвращающее строку (тип — Java.lang.String). В примере вызывается статический метод sum() класса MyUtil. При этом нужно отметить, что в контексте данного параметра может фигурировать любое корректное выражение Java — от вызовов методов до конкатенации строк и ссылок на переменные Java. Интеллектуальные области доступа для всех ссылок вычисляются именно в контексте параметра. Проверка типов выполняется для всех выражений.

Язык DataUx полностью использует все существующие концепции языка MPS Java, в частности выражения и обработку системы типов — эти элементы языка обычно являются чрезвычайно сложными. Использование выражений Java в DataUx, несмотря на простоту, дает пользователю языка дополнительные преимущества. Выражения позволяют гибко адаптироваться к требованиям конкретного случая, не прибегая к какому-либо внешнему, не интегрированному программному коду. Ключевое преимущество также обеспечивается возможностью расширения других языков: сохраняя независимость от синтаксиса языка MPS Java, можно легко ссылаться на концепции, объявленные в этом языке, например, на статический метод, который вызывается в параметре таблицы «Table Summary Line».

Язык DataUx был разработан для одного из наших крупных клиентов — предприятия розничной торговли из Австралии. На стороне заказчика восемь разработчиков реализуют на DataUx все необходимые пользовательские интерфейсы, не затрагивая каких-либо дополнительных аспектов пользовательского интерфейса в обычном коде Java. После успешного определения идеи и структура языка сам язык был разработан за пару дней. Однако на разработку среды выполнения интерфейса пользователя ушло больше времени из-за сложности реализации под несколько разных технологий.

Язык DataUx доступен на GitHub.

Заключение

DataUx — это весьма просто и логично устроенный предметно-ориентированный язык, позволяющий делегировать разработку пользовательских интерфейсов людям без навыков программирования. Крутизна кривой обучения так мала, что какие-либо объяснения практически не требуются. Этот язык также чрезвычайно упрощает понимание и обслуживание существующих пользовательских интерфейсов. Более того, пользовательский интерфейс, заданный с помощью DataUx, не зависит от технологии реализации. Это очень важное достоинство, поскольку технологии пользовательских интерфейсов в наши дни быстро меняются. Отметим, что мы уже предоставляем среды выполнения DataUx для Apache Pivot, JavaFX, vaadin и HTML5. Пользователю достаточно выбрать требуемую платформу, и MPS сгенерирует необходимый код из описаний пользовательского интерфейса на языке DataUx.

DataUx демонстрирует способность JetBrains MPS к комбинированию внешних DSL — в данном случае DataUx расширяет внутреннюю реализацию Java. В DataUx используются важные концепции Java, такие как выражения, при этом обеспечивается привлекательное визуальное представление. Именно такое расширение языка позволяет DataUx взаимодействовать с объявлениями на Java. Обычно языки в MPS могут ссылаться на концепции из других языков, и следовательно, на спецификации, смоделированные на других языках. Проекционный редактор работает с АСД и позволяет создателю языка составлять, многократно использовать и модифицировать языки, но что важнее всего, он позволяет разным предметно-ориентированным языкам взаимодействовать друг с другом. MPS способствует активному взаимодействию между разными предметными областями.

MPS table

Рисунок 1: различные предметно-ориентированные языки для моделирования бизнес-приложений дополняют друг друга

В самом деле, определение пользовательского интерфейса для бизнес-приложения с помощью DataUx — это еще не все. В типичном бизнес-приложении пересекается целый ряд предметных областей, таких как хранение данных (например, запросы и преобразования SQL), бизнес-логика и пользовательские интерфейсы. Поскольку JetBrains MPS обеспечивает взаимодействие между DSL, мы разработали для каждой специализированной части бизнес-приложения отдельный язык (см. рисунок 1). Созданы три узкоспециальных предметно-ориентированных языка, образующих полноценный комплект средств разработки. Таким образом, для каждой конкретной задачи мы предоставляем наиболее выразительный язык, не отказываясь при этом от создания всего бизнес-приложения в пределах единой среды разработки. Для каждой предметной области бизнес-приложения обеспечиваются высокоуровневые абстракции, причем не создается препятствий для интеграции и сохраняется простота формулирования и понимания, а значит, удобство обслуживания и высокое качество в долгосрочной перспективе.

Ссылки и литература

  • DataUX на GitHub
  • JetBrains MPS
  • Kelly and Tolvanen, 2008: Domain-Specific Modeling: Enabling Full Code Generation, Wiley-IEEE Computer Society
  • Fowler 2010: Domain-Specific Languages, Addison-Wesley Educational Publishers Inc