Нам так нравится наш интерфейс, что мы написали его дважды! Эта фраза отразила состояние кодовой базы Tableau примерно в 2017 году, когда каждая новая функция требовала двух реализаций пользовательского интерфейса. С одной стороны был Tableau Desktop, использующий Qt для собственного пользовательского интерфейса для Windows и Mac. С другой стороны, наши веб-продукты: Tableau Online, Tableau Server и Tableau Public — предоставляли почти такой же интерфейс для Интернета, созданный с помощью JavaScript и HTML.
Мы решили дать разработчикам возможность выполнять итерации быстрее и сосредоточиться на предоставлении пользователям единого высококачественного опыта. Решение этих задач также требовало, чтобы мы продолжали обеспечивать дополнительную ценность как наших веб-продуктов, так и продуктов для настольных компьютеров — это означало, что что-то вроде массовой миграции нашего настольного приложения на Электрон было исключено.
Мы решили, что лучшим способом продвижения вперед будет размещение пользовательского интерфейса на основе браузера в Tableau Desktop. На тот момент в нашем десктопном продукте уже было несколько функций, использующих пользовательский веб-интерфейс, но по-настоящему совместное использование пользовательского интерфейса на разных платформах потребовало определенных усилий.
В этом посте будет определен общий пользовательский интерфейс в контексте Tableau, наш подход к реализации общего пользовательского интерфейса и то, как все выглядит сегодня.
Что такое общий пользовательский интерфейс?
Общий пользовательский интерфейс — это пользовательский интерфейс на основе браузера, который используется как в нашем браузере, так и в собственных приложениях. Для наших серверных продуктов общий пользовательский интерфейс работает локально вместе с остальным клиентским кодом в любимых веб-браузерах наших пользователей. Для Tableau Desktop мы размещаем этот пользовательский интерфейс внутри QWebEngineViews, виджета веб-представления на основе Chromium. Более интересные примеры общего пользовательского интерфейса соответствуют двум конкретным критериям: двусторонняя связь между собственным кодом и размещенным пользовательским интерфейсом и параллельная интеграция размещенного пользовательского интерфейса и существующего собственного пользовательского интерфейса.
Давайте посмотрим на конкретный пример Tableau: расширения панели инструментов. Эти расширения позволяют написанному пользователем JavaScript взаимодействовать с несколькими собственными визуализациями.
На приведенном выше изображении Tableau Desktop выбранный объект панели мониторинга является расширением, работающим внутри веб-представления. При публикации на сервере Tableau будет работать точно такой же код расширения, в блаженном неведении о том, где он размещен. Это расширение органично сочетается с остальным нашим внутренним пользовательским интерфейсом на обеих платформах благодаря компонентам React, доступным через проект GitHub: Tableau UI. На рабочем столе окружающий пользовательский интерфейс является родным — реализован с помощью QWidgets со стилями, специфичными для Tableau, или визуализациями, отображаемыми в виде изображений.
Расширения панели мониторинга не существуют изолированно от остальной части приложения; двусторонняя связь с функциями продукта является неотъемлемой частью их функциональности. Extensions API предоставляет доступ к свойствам панели инструментов и для выдачи команд нативному коду. Например, API предоставляет функции для изменения параметров или фильтров, применяемых к панели мониторинга.
Общий пользовательский интерфейс отлично подходит для расширений, в которые наши клиенты могут легко вставлять свой собственный контент. Однако применение общего пользовательского интерфейса не ограничивается размещением внешнего контента; ряд встроенных элементов управления (диалоги, боковые панели и даже целые вкладки) теперь реализованы с помощью этой техники.
Реализация общего пользовательского интерфейса
Tableau заложил основу для внедрения общего пользовательского интерфейса, раскрутив команду, чтобы сосредоточиться на инфраструктуре, лежащей в основе компонентов, ориентированных на пользователя.
Основное внимание этой команды уделялось разработке «абстракций платформы». Эти API-интерфейсы предоставляют общий интерфейс для пользовательского интерфейса на основе браузера, скрывая информацию о том, на какой платформе (т. е. браузере или настольном клиенте) он в настоящее время размещен. Один из таких API обеспечивает доступ к общим компонентам пользовательского интерфейса, таким как контекстные меню. Пользовательский интерфейс запрашивает контекстное меню для общего интерфейса, а реализация абстракции под капотом материализует правильное собственное меню. Другие API предоставляют возможность настраивать диалоги, перетаскивать через общую/собственную границу или взаимодействовать с базовой рабочей книгой Tableau.
Эти абстракции было легко реализовать для нашего браузерного клиента: во время выполнения мы динамически загружаем библиотеки JavaScript, которые реализуют интерфейсы. Затем JavaScript общего пользовательского интерфейса может напрямую обращаться к этим библиотекам.
На десктопе реализация немного сложнее, так как нативная функциональность предоставляется почти исключительно фреймворками C++. Мы воспользовались другой библиотекой Qt, QWebChannel, чтобы предоставить коду браузера почти прозрачный доступ к родным объектам C++ QObject. Каждый из объектов, которые мы назвали объекты взаимодействия в коде Tableau, предоставляет набор методов и уведомлений, примерно соответствующих поверхности каждого из API-интерфейсов абстракции. Тонкие оболочки JavaScript адаптируют любые шероховатости, чтобы они соответствовали API-интерфейсам, предоставляемым клиентскому коду. На стороне C++ эти объекты взаимодействия вызывают соответствующую бизнес-логику для выполнения любых интересных операций.
Этот подход хорошо сработал для нас. Реализации исходной команды минимально необходимых API были расширены, и с тех пор другие команды добавили свои собственные общие абстракции. Этот уровень позволил разработчикам пользовательского интерфейса сосредоточиться на предоставлении клиентам единого опыта, унифицированного для разных продуктов.
При общей механике работы общего пользовательского интерфейса необходимо было решить множество более мелких проблем: правильная упаковка содержимого пользовательского интерфейса для обеих платформ, объединение веб-представлений для быстрого представления общих диалогов на рабочем столе, улучшение шагов в этом новом рабочем процессе разработчика и обучение функциональных групп. на всех кусках. Это был повторяющийся процесс, основанный на плодотворном партнерстве с рядом команд, создающих свой пользовательский интерфейс с помощью этой новой системы.
Заложив основу, нам еще предстояло выяснить, как направить корабль в сторону этой новой парадигмы развития. В Tableau есть множество различных элементов пользовательского интерфейса, взаимодействий и диалогов для создания визуализаций и информационных панелей. Мы решили, что поэтапный подход — лучший способ достичь нашей цели — единой кодовой базы пользовательского интерфейса. Мы разработали следующие рекомендации для поощрения внедрения общего пользовательского интерфейса в масштабах всей организации:
- Новые функции должны создаваться как общий пользовательский интерфейс.
- Большие функциональные изменения в существующих автономных компонентах пользовательского интерфейса должны быть переписаны как общий пользовательский интерфейс. Мы доверили командам проработку нюансов, связанных с возможностью преобразования более крупного или более тесно связанного пользовательского интерфейса.
Как только команды освоились с одной или двумя новыми функциями, было несложно убедить их продолжать создавать свой пользовательский интерфейс таким же образом. Им нужно двигаться быстрее и сосредоточиться на забавных вещах — создании новых функций для клиентов.
Команды в Tableau уже создали десятки функций пользовательского интерфейса с общим пользовательским интерфейсом, и это стандартный подход для создания новых функций.
Таблица сегодня
Итак, где мы сейчас? На высоком уровне у нас есть как общий, так и собственный пользовательский интерфейс, живущие бок о бок на рабочем столе — можете ли вы определить, какие части пользовательского интерфейса какие?
Да, у нас по-прежнему есть две базы кода для просмотра и создания визуализаций, но с каждой новой частью общего пользовательского интерфейса мы продолжаем снижать нагрузку на обслуживание, которую мы несли с двумя версиями всего. Кроме того, наши разработчики могут двигаться быстрее — настраивая и улучшая один интерфейс, а не пытаясь дублировать его в двух местах.
Наше стремление к единому общему пользовательскому интерфейсу также дало несколько дополнительных преимуществ. Что касается настольного продукта, где повторная компиляция кода C++ стала препятствием для быстрой итерации, разработчики теперь могут настраивать свой пользовательский интерфейс с почти мгновенной обратной связью в автономной среде браузера. Также проще протестировать его интеграцию с основным продуктом — их код можно повторно загрузить в веб-представления хостинга, даже не перезапуская Tableau. В приложениях строгие границы, установленные API-интерфейсами абстракции, привели к более модульному коду, четко разделенному по функциональным линиям и линиям владения.
Мы рады облегчить создание новых функций для нашей команды разработчиков. В конечном итоге это возвращает нашу команду к тому, для чего мы здесь на самом деле: помогать людям видеть и понимать свои данные.