Блеск и нищета GWT (или почему я не верю в Dart часть 2)

:

Одной из самых обсуждаемых тем на Хабре стало объявление Гугла о создании нового языка Dart, претендующего на то, чтобы полностью заменить Javascript.
Для Гугла это не первый проект подобного рода. С 2006 года существует Google Web Toolkit, позволяющий создавать веб-приложения полностью на Java. На нём, например, работают Adwords и Google Wave.
Но, не смотря на красивые обещания и очень серьезные, особенно по меркам 2006 года, возможности, GWT так и «не взлетел». Да, проект поддерживается и развивается, но сообщество разработчиков так и не сложилось. Сторонних компонентов мало, они не поддерживаются, знакомых с системой программистов найти сложно.
Практически все «плюшки» языка Dart, которые сейчас рекламирует Google, уже были реализованы в Webtoolkit-е. В этой статье я хочу проанализировать достоинства и недостатки GWT в попытке рассмотреть мутные очертания будущего Dart.

Цели и задачи


С точки зрения программиста, для создания современных веб-приложений приходится использовать целый зоопарк технологий: HTML, CSS и Javascript для клиентской части; SQL, PHP и часто какой-нибудь шаблонизатор типа Smarty для серверной; данные перемещаются по сети при помощи XML, JSON или какого-нибудь велосипеда.

Это классический Unix-way: для каждого участка работ применяется свой инструмент. Такой подход обеспечивает огромную гибкость. Если какой-то конкретный инструмент вам не нравится, вы можете заменить его на другой: PHP на питон, SQL на NoSQL, HTML на Flash.

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

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

GWT, как и Dart, призван решить эти проблемы. В случае GWT из всего зоопарка остаётся только Java и небольшой кусок CSS, отвечающий только за шрифты и цвета (но не за расположение элементов). Всё остальное либо сокрыто в глубине библиотек, либо используется на уровне стандартной заглушки (HTML с единственным div-ом, в который потом программно будет вставлен весь интерфейс).

Кроме зоопарка технологий есть еще зоопарк браузеров во главе с IE. Даже сейчас, обрабатывая банальное событие нажатия клавиши (onkeypress), в javascript приходится проверять версию браузера, потому как разные браузеры вызывают эту функцию по-разному. Тогда же, в 2006-ом, приходилось использовать огромное количество костылей просто для того, чтобы страница выглядела более-менее одинаково. GWT красиво решал и эту проблему.

Как это работало


Весь GWT-проект писался в одной среде и на одном языке — теплом ламповом Java с MVC и паттернами. Это был почти полноценный Java в том плане, что эмулировалась почти вся стандартная библиотека, но конечно просто перенести весь код из десктопного приложения было нельзя. Google даже выпускал специальную сборку редактора Eclipse, в котором всё, включая отладку, работало из коробки.

На серверной стороне всё было похоже на обычные Java Server Pages с подключенными библиотеками GWT. Но так как JSP-хостинг ещё нужно было найти, в настройках всего этого хозяйства так или иначе приходилось разбираться. Впрочем, серверную часть можно было создать на чем угодно, но все преимущества единого языка при этом, разумеется, терялись.

Клиентская же часть компилировалась из Java в Javascript, причем под каждый браузер компилировался и динамически подгружался свой файл. Это позволяло избежать вороха проверок версии браузера и в целом работало очень хорошо.

HTML как таковой не использовался. Теоретически можно было сверстать свою HTML страницу и затем программно вставить в нужные места элементы управления, но всё же мейнстримом было создание полностью динамического интерфейса целиком на Java. Были даже полу-рабочие графические редакторы, позволяющие рисовать интерфейс мышкой почти как в редакторе форм .Net или там Delphi. Расположение и размеры элементов задавались программно, на долю CSS оставались только шрифты, цвета и мелкие рюшки типа скругленных уголков.

В-общем, в теории всё выглядело очень здорово.

Практика


На практике всё было не так радужно.

В автоматически создаваемых элементах интерфейса использовалась махровая табличная верстка. Любой прямоугольный блок генерировал таблицу с одной ячейкой. Панель (объединение нескольких блоков) представлялось таблицей с одной строкой или столбцом. Диалоговое окно представляло собой нагромождение вложенных панелей. На выходе получалась каша из таблиц довольно большой степени вложенности, разобраться в которой было практически невозможно. Браузеры тоже без особой радости пережевывали получившийся гигантский DOM.

Я совершенно не приверженец семантической верстки, я понимаю, что реализовать программную генерацию HTML с позиций классического ООП, да ещё так, чтобы это работало одинаково во всех версиях IE, да еще в 2006-ом году, можно было только на таблицах. Однако то, что получилось в GWT — оно просто тормозило. Оно до сих пор тормозит, даже в Хроме, даже на современных процессорах, даже на страницах с всего-лишь сотнями виджетов.

Сверстать свой HTML и скормить его GWT на практике оказалось также не очень-то просто. Это вам не jQuery, где вы можете легко манипулировать DOM и генерировать свой HTML так, как считаете нужным. Вы могли, в теории, сэкономить пару верхних уровней таблиц, но совсем от них отказаться — никак. Хуже всего дела обстояли с взаимодействием GWT и нативного Javascript.

В теории GWT поддерживает JSNI — что-то а-ля ассемблерных вставок в Си, только с Javascript в роли ассемблера. Но работает это только в одну сторону. То есть вызвать какую-то внешнюю js-библиотеку из GWT вы можете без проблем. А вот наоборот… Да что там javascript, банально повесить свой onclick на html-элемент, чтобы перехватить его в GWT — такое шаманство не было описано в мануале и, похоже, вообще не было предусмотрено разработчиками. Сделать-то это можно было, но с помощью трюков Javascript, а не GWT.

Гугловцы старались полностью перекрыть все различия браузеров, но всё же до конца этого сделать не смогли. Оставались (по крайней мере в версии 2.2, дальше я уже не работал) кое-какие тонкие моменты, которые даже в javascript-е относятся к «шаманствам», таким как запрет выделения текста при смазанном клике мышкой. Но это уже придирки.

Концептуальные проблемы


Надо признать, что в целом GWT работал. Может местами медленно, может кое-где коряво и не до конца, но работал. Главная его беда в том, что пресловутая скорость разработки больших приложений на GWT оказалась намного ниже, чем на стандартном зоопарке технологий. И если проблемы предыдущего раздела Dart решит, то проблемы со скоростью разработки — нет.

Все парадигмы программирования — классическое ООП, прототипная, процедурная, реляционная, функциональная — все они полны по Тьюрингу и позволяют решить один и тот же класс задач. Сказать, что ООП это самая лучшая парадигма — всё равно что сказать «я ламер». Весь вопрос в том, какая парадигма позволяет быстрее решить конкретную задачу.

Java, и с ним весь классический ООП, зиждется на идее разделения классов и собственно объектов — экземпляров класса. Это подходит всяким бизнес-приложениям, которым нужно обрабатывать миллионы строк накладных, платежей, остатков на складах и так далее. Классов там относительно мало, а вот объектов — много.

В вебе всё не так. На одной странице у вас есть всего одна форма входа, одна корзина, один текущий пользователь и одна кнопка «заплатить бабло». Городить для каждого из этих элементов отдельный класс и потом создавать по одному экземпляру каждого класса… а зачем? Практика показывает, что чаще всего разделять классы и объекты в вебе просто не требуется.

Ок, скажет последователь Банды Четырёх, а что же будет, когда потребуется вторая кнопка «бабло», но с перламутровыми пуговицами? В Javascript-е всё вот именно так и будет: вторая кнопка, созданная по прототипу первой, но с пуговицами. В классическом же ООП будет отдельный класс, но это полбеды; главное — будет рефакторинг с выделением интерфейса и написанием кучи кода, который вообще ничего не делает, а просто поддерживает структуру классов. А всё из-за статической типизации, которая не позволяет просто так использовать вместо «просто кнопки» «кнопку с перламутром».

Я четыре года писал на GWT, и я могу уверенно сказать, что для веба утиная типизация лучше статической. И влияет оно только на скорость компиляции, а не на скорость исполнения кода.

GWT делали истинные фанаты классического ООП, не позволявшие никаких отклонений от Святых Паттернов. Если кто-то решил, что нельзя использовать инлайн-стили элементов, значит нельзя и точка. Нет такой функции — изменить стиль, и не будет, делайте всё CSS-классами. Вообще, конечно, в большинстве случаев инлайн-стили действительно не стоит использовать, но как, например, реализовать анимацию? Создавать 20 классов вида .opacity03 { opacity: 0.3 }?

Блин, в GWT есть компонент типа Grid (элемент управления типа таблица, в которой можно править ячейки), в котором полностью реализован свой MVC. В визуальном элементе управления… Если вам нужно сделать табличку 2х3, вам нужно описать модель, контейнер и далее по списку. Строк так на 200 кода:)

Если эта же команда делала Dart… ну, не знаю. Сейчас не 2006-ой год, Javascript окреп, устаканился и оброс библиотеками уровня jQuery. Я не вижу ни одной причины, по которой Dart «выстрелит». Не смотря на всю мощь Гугла.