Универсальный интерфейс пользователя в АСР Fastcom

Р. Абдрахимов,
«ФОРС Телеком»,
ведущий эксперт

От редакции журнала “FORS Magazine”: "В силу объективных обстоятельств ведущий эксперт "ФОРС — Телеком" Р. Абдрахимов не смог присутствовать на семинаре и выступить с подготовленной совместно с Д. Козиком презентацией «Визуальные интерфейсы пользователя — инструментарий и архитектура». Ниже публикуется основная статья, на базе которой была сформинована презентация".

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

Другими словами, возникает вопрос: Что бы такого придумать, чтобы при появлении нового средства разработки интерфейса не переписывать всё, что наработано за десятилетия существования своего проекта, а желательно найти такое средство, чтобы вообще ничего не переписывать?

Нас, разработчиков Автоматизированной Системы Расчётов (АСР) Fastcom, как тиражной универсальной системы с большой историей, не мог не беспокоить вопрос конструирования и применения единого принципа реализации интерфейса пользователя. Самое естественное было бы выбрать самую подходящую из имеющихся на рынке систем разра-ботки интерфейса. Обычно так и поступают — выбирают систему, с помощью которой быстрее всего достигаются поставленные цели. Но в этих случаях цель, как правило — выполнение одного конкретного контракта. В нашем случае цель была гораздо шире: Выполнение множества контрактов с разнообразнейшими требованиями, меняющимися со временем согласно коньюктуре бизнеса и изменению конкурентной среды.

Вот основные требования, которым должно удовлетворять средства для разработки и отображения интерфейса:

  1. Интерфейс должен легко дорабатываться для отображения на любых существующих и появляющихся в будущем интерактивных системах взаимодействия с пользователем.
  2. Интерфейс должен быть гибким, легко настраиваемым под потребности конкретного заказчика и, более того, настраиваемым под меняющиеся со временем потребности конкретного заказчика.
  3. Интерфейс должен уметь подстраиваться к данным, то есть автоматически изменяться в зависимости от отображаемых или вводимых данных
  4. И, наконец, разработчики бизнес-логики (прикладники) должны пользоваться таким инструментом построения интерфейсных форм, который бы не зависел от какой бы то ни было системы взаимодействия с пользователем

Рассмотрим эти требования поподробнее

Адаптируемость

В самом начале проекта Fastcom мы сделали ошибку, целиком заложившись на Oracle Forms. Тогда казалось, что этот инструмент будет развиваться вечно, да и ни о какой гибкости речи тогда не шло, так как бизнес-логика была «размазана» между клиентской и серверной частью, а любая новая бизнес-потребность решалась созданием новых форм, благо инструмент позволял это сделать довольно быстро. Со временем стало ясно, что Oracle не собирается развивать и поддерживать 2-х звенную реализацию своего продукта Oracle Developer и, в частности, Oracle Forms. Вплотную подошла необходимость переписывания всего интерфейса АСР под другой, более современный и более популярный продукт.

Раз это стало неизбежным, тогда не хотелось бы в последствии наступить на те же грабли и с новым продуктом. В итоге, возникло требование адаптируемости — система интерфейса должна состоять из двух частей: независимого языка описания интерфейса разработчиками бизнес-логики и движка, который это описание интерпретирует при помощи наиболее подходящей системы взаимодействия с пользователем.

Что значит «подходящей»? Значит, соответствующей текущим технологиям, моде, а иногда — прямым требованиям конкретного заказчика. Как только возникает потребность перехода на новый «движок», то не возникает необходимости переписывать весь интерфейс, перетряхивать все формы, адаптировать бизнес-логику. Достаточно дописать драйвер, интерпретирующий язык описания интерфейса для адекватного отображения его новым «движком».

Гибкость

Это требование продиктовано особенностями той сферы деятельности, которую мы автоматизируем. Предприятия связи очень сильно отличаются друг от друга по набору и способу предоставления услуг. На нашей практике не было ни одной пары технологически полностью сходных предприятий. Более того, конкурентная среда, в которой работает каждый наш заказчик, оказывается уникальной и заставляет предприятие постоянно адаптироваться под её новые требования. Согласитесь, старый принцип «создать новые формы под новые требования» в условиях постоянного увеличения количества заказчиков, несовпадения модели их динамично развивающихся бизнесов привёл бы к экспоненциальному росту количества сотрудников и технологий управления.

Адаптивность к данным

Итак, бизнесы наших заказчиков разные, да ещё и постоянно изменяющиеся. Экстенсивное создание новый форм — путь неприемлемый. Замедлился бы процесс внедрения, усложнилась бы поддержка, увеличился бы рост накладных расходов. Значит, формы должны, где надо, уметь самостоятельно адаптироваться к настройкам, справочникам и каталогам — то есть, к данным.

Простейший пример, когда в зависимости от типа объекта одни действия с ним возможны, другие бессмысленны, а третьи вообще вредны. Такой подход позволит переложить большую часть процесса внедрения на специальных людей, не разработчиков, которые будут действовать исключительно через изменение данных. А после внедрения, в процессе эксплуатации, работа по адаптации АСР к изменению бизнеса может выполняться силами администраторов на местах.

Динамические свойства вместо событий

Всё до сих пор описанное соответствует заявленным возможностям нескольких Систем построения интерфейсов, присутствующих на рынке. Но вот 4-е требование, особенно в свете первых трёх, является невыполнимым ни для одной известной нам интерфейсной системы. Дело в том, что в сложных формах разработчику — прикладнику волей-неволей приходится реагировать на события в форме. Причём реакция, посредством создания триггеров, обрабатывается инструментами клиентской части, на клиентском языке. Таким образом, мы каждого разработчика бизнес-логики заставляем знать клиентский язык отображения интерфейса и ровно столько языков, сколько используется движков. Но всё ещё хуже — интерактивные возможности каждого движка различаются. Не всегда возможно реализовать один и тот же функционал одинаковым способом.

Традиционная разработка интерфейса приложений предполагает, что прикладник сначала опишет и создаст все необходимые для реализации прикладной функции интерфейсные элементы, а потом начнёт его «оживлять», создавая или описывая обработчики различных событий: Нажатия на разные клавиши, клики мышкой, изменения размеров, перетаскивания, расширения, обмен местами и т.д. и.п.

Естественно, что обработчики событий не могут программироваться на языке сервера базы данных. В лучшем случае только на языках сервера приложений или вообще на языках клиентской среды. А в этой области мода и веяния быстро могут сменить всё и кардинально.

Вот тут то и возникла идея перейти от событийно ориентированного программирования интерфейсов к свойство ориентированному программированию. Разработчик имеет инструмент описания элементов форм и их свойств, но свойства могут быть не только статическими, но и динамическими. То есть, имеет средство указать — от чего зависит свойство и как оно должно меняться в зависимости от текущих обстоятельств. По сути, описание динамических свойств является декларативным языком программирования. Самым логичным решением было бы воспользоваться уже имеющимся декларативным серверным языком — SQL.

Вот пример свойства — заголовка формы, отображающей информацию по договору:

Договор №{Select Contract_No from CONTRACTS where ID=0{CONTRACT_ID}}

Здесь есть статическая часть — «Договор №» и динамическая часть в фигурных скобках, содержимое которой является SQL-запросом, использующим ссылку на входной параметр формы — CONTRACT_ID. Перед выполнением запроса все ссылки заменяются их текущими значениями, а потом выполняется запрос

Select Contract_No from CONTRACTS where ID=057264

Результат выполнения запроса и будет значением динамической вставки:

Договор №R-23/183.2

Если запрос не вернёт ни одной строки — ошибки не возникнет. Динамический элемент в этом случае должен вернуть пустую строку. Если запрос вернёт более одной строки — ошибки тоже не будет. Все строки результата в этом случае слепятся в одну строку. Это, кстати, можно использовать для пользы.

Внутри динамических элементов можно использовать другие динамические элементы любой глубины вложенности. Вычисление (парсинг) динамики будет начинаться с самых вложенных элементов. Результат вычисления динамики тоже может содержать динамические элементы. И они будут вычислены в соответствующем порядке.

Теперь допустим, что динамически вычисляется статусная строка формы-списка договоров. Эта строка должна отображать подробности того договора, на котором находится курсор (текущая строка списка):

{Select Decode(Count(*), 0, 'Нет объектов', 'Объектов: '||Count(*)) from OB-JECTS where CONTRACT_ID=0{ID}}

Здесь {ID} –ссылка на контекстную переменную, означающую внутренний идентификатор текущей записи о договоре.

По идее, данное свойство должно пересчитываться каждый раз, когда меняется текущая строка списка договоров. В традиционном программировании разработчик-прикладник должен был создать обработчик события — изменение текущей строки и программно перевычислить свойство. Более того, он должен был ещё обработать события, которые тоже могли бы привести к изменению статусной строки (например, закрытие формы с деталями — объектами договора). Что плохо — в процессе развития системы могут появиться факторы, изменяющие информацию из статусной строки, но не учтённые прикладником в момент разработки интерфейса.

Разделение ролей разработчиков

В новой концепции свойствоориентируемого программирования не забота разработчика — прикладника думать о том, когда и как будет пересчитано динамическое свойство. Это будет заботой разработчика клиентского движка — интерпретатора свойств элементов интерфейса.

Так мы снимаем заботу об актуализации данных (с учётом минимизации затрат производительности) с разработчиков прикладной системы и переносим их на профессиональных, выделенных для этого сотрудников — инфраструктурщиков.

Как видно, у нас наметилось чёткое разделение ролей между двумя группами разработчиков:

  1. Большая по численности группа, которую мы называли «Прикладники», состоящая из специалистов в прикладной области. Они погружены в предметную область и очень хорошо знают одно конкретное средство построения баз данных и реализации бизнес–логики. К примеру: Oracle + SQL + PL/SQL.
  2. Меньшая группа разработчиков занята исключительно инфраструктурными проблемами проекта. Этим программистам вовсе не обязательно хорошо знать автоматизируемую проектом предметную область и не нужно быть доками в базах данных. Зато они всегда в курсе самых новых веяний в области систем, средств и языков программирования, следят за модой и тенденциями в интерфейсах и эргономике. Такую группу будем называть «Инфраструктурщики».

Прикладники в вопросах интерфейса ограничиваются тем ЧТО надо показать, а Инфраструктурщики озабочены тем КАК надо показать.

Основная психологическая проблема — чтобы одни не лезли в епархию других, так как испокон веков любого программиста хлебом не корми — дай подвигать по канве кнопочки и поля!

Основная техническая проблема — дать Прикладникам средство строить зависимости свойств элементов интерфейса друг от друга. При этом такое средство не должно требовать знания других языков, кроме Oracle SQL и/или PL/SQL

Многоликие свойства

Что же нам дал переход от процедурного событийно ориентированного программирования к декларативному свойство ориентированному средству описания интерфейсных форм? Теперь разработка и/или адаптация интерфейса под новый движок никак не затрагивает бизнес-логику приложения. Каждый движок интерпретирует свойства так, как умеет максимально адекватным способом.

Отсюда следствие — свойства элементов интерфейса должны быть максимально абстрактными, такими, чтобы их смогли проинтерпретировать все движки. К примеру, не должно быть свойств «шрифт», «цвет» и т.п., так как могут быть движки без возможности реализовать тот или иной шрифт и даже монохромные, не способные реализовать цвет. Максимум что тут возможно — определить свойство «роль» (со значениями «внимание», «опасность», «успех», «отключено» и т.п.), которое каждый движок будет интерпретировать как умеет, но адекватно указанному значению.

Ещё один принцип выявился уже в процессе нескольких реализаций движков универсального интерфейса: нужно избегать количественных свойств (что-то в штуках, сантиметрах, пикселях и т.п.). Либо нужно обходиться качественными свойствами (например, ролями) или количественные свойства отдавать на откуп автоматического вычисления при помощи «интеллектуальных» усилий движков. Понятно, что в некоторых случаях «интеллектуальность» только вредит. Значит, автоматическое вычисление не может работать без возможности переопределения результатов его усилий в персональных настройках пользователей. К примеру, автоматически вычисленную ширину столбца пользователь может изменить (расширить, уменьшить) и этот факт Система запомнит, чтобы учесть при следующем отображении формы пользователю.

Динамические свойства вычисляются только в момент применения (например, состав и названия пунктов локального меню только непосредственно перед его отображением). Если изменились обстоятельства, влияющие на значение динамического свойства, то немедленно оно будет пересчитано и результат применён. К примеру, если заголовок Списка зависит от текущей строки, то при переходе от строки к строке свойство «Заголовок» будет пересчитываться. Актуализация значений динамических свойств — забота разработчика движка, а не разработчика бизнес-логики.

Заботу о том, чтобы все свойства всех интерфейсных элементов всегда соответствовали декларациям, заданным в динамических элементах, мы с прикладников сняли, но при этом лишили их возможности реализовывать особенные капризы заказчиков. Например, если заказчик скажет, что для отображения именно этой информации и именно при этих обстоятельствах необходимо отображать форму не в прямоугольном, а в овальном окне, то прикладник просто не будет иметь инструментов для этого. Конечно, если такие случаи будут происходить часто, или если заказчик будет настойчив и/или щедр, то прикладники и инфраструктурщики договорятся и выработают недостающий механизм декларации для реализации требований заказчика.

В нашем примере, скорее всего, будет добавлено ещё одно свойство окна — форма отображения (прямоугольное, треугольное, овальное и т.д.). Причём, так как это новое свойство будет подвержено тому же механизму вычисления и применения свойств то в нём также будет возможно использование динамики, которая в нужный момент будет актуализироваться. Так что теоретически можно так декларировать это свойство, что форма окна менялась на ходу в зависимости от тех или иных обстоятельств.

По ходу мы получили ещё одно глобальное преимущество — унификацию. Мало того, что все интерфейсные модули будут выдержаны в одном стиле, но у них будет наличествовать общая (инфраструктурная) функциональность.

Реализация отображения

Со стороны прикладников есть следующие способы создания интерфейсных форм.

  1. Программный. Существует серверный пакет, посредством которого, процедурами, создаются интерфейсные элементы: сами формы со свойствами, столбцы, поля, действия, входные и выходные параметры, группы элементов и т.п. Для каждого элемента указываются свойства, значения которых отличаются от стандартных (значений по умолчанию).

    Все эти действия выполняются в отношении некоторой Рабочей области — структуры времени жизни сессии Oracle. После создания всех элементов управление передаётся клиенту с указанием ID Рабочей области. Клиентский движок начинает отображать форму, интерпретируя указанные свойства её элементов.

    Такой способ наполнения рабочей области незаменим, если интерфейсная форма собирается на лету, «с миру по нитке», используя для этого пополняемые справочники, параметры и настройки Системы.
  2. Метаданные. Существует структура данных, отражающая иерархию интерфейсных элементов. Интерфейс к этой структуре данных позволяет быстро создать (при помощи набора визардов) необходимые элементы интерфейса и указать свойства, отличающиеся от значений по умолчанию.

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

    Этот способ наполнения рабочей области очень хорош в случае разработки интерфейса к обычным таблицам базы данных. И чем проще структура данных, тем быстрее создаётся к ней интерфейс.
  3. Комбинированный. Сначала в рабочую область загружаются метаданные, а потом программным способом производится добавление и/или замена недостающих элементов интерфейса и/или свойств элементов интерфейса.

В конечном итоге клиентский интерфейсный движок оперирует конкретными уже вычисленными значениями свойств интерфейсных элементов, интерпретируя их согласно разумению программистов–инфраструктурщиков и в соответствие с возможностью свой среды.

Для преобразования динамических свойств в статические служит специальный драйвер, реализованный как серверный хранимый код. Драйвер разрабатывают и поддерживают инфраструктурщики. Задачи этого драйвера такие:

Разновидности клиентских движков

Задача клиентской части — получить с сервера набор интерфейсных элементов с текущим значением их свойств и правильно (адекватно) проинтерпретировать их, обеспечив функционал, задуманный программистом-прикладником.

Всё, что нужно клиентской части — возможность установления персонального сеанса с сервером Oracle, периодические запросы (по событиям) и получение набора изменённых свойств элементов интерфейса.

Для функционирования интерфейса в целом не нужно промежуточное, среднее звено. Вся бизнес-логика реализована на сервере, поддержка интерфейса (рабочие области интерфейсных форм) тоже осуществляется на сервере, клиентская часть только интерпретирует конечные результаты — ответы сервера.

При отсутствии среднего звена и условии минимизации трафика получается, что данный интерфейс реализует концепцию 2-х звенного тонкого клиента. Конечно, некоторые реализации клиентских движков могут потребовать наличия WEB–серверов, но никакой логики приложения в себе они не несут и их можно рассматривать как элементы транспортной системы.

Лучше всего в данной концепции реализуется клиентская часть в классической полно-функциональной среде разработки интерфейса типа Delphi. Однако нами поначалу был реализован движок на Oracle Forms, хотя он в самой меньшей степени подходит для этой задачи, так как имеет скупые средства реализации свойств элементов интерфейса, не поддерживает создание на ходу интерфейсных элементов и не реализует замену многих свойств элементов в Run Time режиме.

Тем не менее, клиентский движок был успешно реализован, так как его неоспоримым преимуществом была совместимость с той частью тиражной системы, которая ещё не была переведена на новый интерфейс и оставалась набором форм, сгенерированных посредством Oracle Designer. Эта реализация позволила осуществить постепенный переход к новому, универсальному интерфейсу.

Надо ли говорить, что одновременно может работать несколько движков, реализованных на разных технологиях, но интерпретирующих одни и те же свойства интерфейсных элементов из рабочих областях форм.

В настоящее время основные усилия направлены на реализацию клиентской части, работающей на любом интернет–браузере без использования каких-либо дополнительных программ и средств. Обращаю внимание, что хотя всё приложение доступно пользователю через обычный браузер, интерфейс остаётся 2-х звенным тонким клиентом!

WEB-движок

Интерфейсный клиентский модуль, функционирующий посредством интернет–браузера, реализован на языке JavaScript с использованием технологии AJAX.

Самая большая проблема, с которой пришлось столкнуться — отсутствие на рынке средства, позволяющего держать персональное соединение между WEB-страницей браузера и сервером Oracle, не сбрасывая пакетные переменные, сохраняя курсоры и поддерживая «длинные» транзакции. Исходя из этого, было принято решение создать и поддерживать своё собственное средство, которое полностью удовлетворяло бы необходимым требованиям. Такое средство было создано — специальный модуль к HTTP-серверу Apache 2.2 под платформы Windows и Linux.

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

Обмен данных с сервером производится посредством AJAX-запроса. Ответы от сервера приходят в виде фрагментов программы на языке JavaScript, которые тут же и выполняются. Возможны синхронные и асинхронные запросы. Синхронные запросы применяются тогда, когда невозможно продолжение работы без ответа сервера, например, при обработке событий: Открыть/закрыть форму, изменить текущую строку/поле, выполнить конкретное действие. Асинхронные запросы применяются в случаях, когда получение ответа можно и отложить. Яркий пример — отображение итоговых данных. Запрос на их получение идёт сразу за отображением первой порции строк списка. Если до следующего события результат с итоговыми данными не был получен, то запрос на них прерывается для обработки возникшего события.

Реальное тестирование продукта показало, что приемлемая скорость работы достигается даже на интернет–канале в 64 Кбит/сек. На более низких скоростях тестирование просто не проводилось.

С самого начала было поставлено и реализовано требование кросбраузерности WEB-движка. То есть, приложение одинаково работает на всех наиболее распространённых интернет-браузерах. Тестирование проводилось на Mozilla Firefox 5.0, Google Chrome 5.0, Internet Explorer 6, 7, 8, Opera 10. Единственное отличие — в скорости интерпретации JavaScript разными браузерами. Разница между самым быстрым (Google Chrome) и самым медленным (Internet Explorer 6) браузером — в два раза.