Типобезопасный биндинг данных для UI компонентов

Хныканье про UI биндинги

Мне по долгу службы приходится иметь дело с UI компонентами и их API. Я постоянно разрабатываю их, расширяю и интегрирую существующие, пишу к ним тесты и документацию. За 5 лет я видел много разных подходов и хотел бы обсудить одну существенную деталь из мира UI разработки.

Связь с данными — довольно болезненный вопрос при работе с различными полями ввода. Мы постоянно используем различные сложные абстракции — Property, DataSet, DataSource, Binding,  Adapter и ещё в довесок кучу костылей вроде ручной реализации реакции на изменение данных в модели и в компоненте. При этом основных подходов для подключения источника данных к UI компоненту всего два:

  1. Реализация интерфейса Property { get; set; }
  2. Подключение свойства объекта при помощи рефлексии по имени

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

suspension_bridge

Давайте помечтаем. Ах вот если бы у нас был такой волшебный оператор, который бы в статически типизированной форме вернул нам по члену класса его имя.

Мы бы смогли использовать его так:

Это было бы идеально! Рефакторинг такого кода очень прост, компилятор проверяет существование свойства и его модификатор доступа — просто подарок для UI фреймворков!

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

Groovy

Groovy — динамический язык для платформы Java, интересен хорошей поддержкой Мета-программирования. В нём есть возможность получения ссылки на метод при помощи специального оператора &. Его можно использовать для получения имени свойства по соглашениям (к сожалению в мире Java по-прежнему нет свойств).

Это всё хорошо, за исключением маленькой проблемы. Groovy — динамический язык. Компилятор даже не заметит вашей опечатки в имени свойства.

 C#

Начиная с версии 3.5 в C# появилась поддержка деревьев выражений в рамках технологии LINQ. Вкратце — это возможность во время исполнения вместо переданного выражения манипулировать его синтаксическим деревом. Как если бы компилятор превратил выражение в строку и подставил её вместо исходного кода (только развернул бы ещё все вызовы).

Использование несколько громоздко, но ведь это замечательно работает! Есть небольшой минус в виде больших накладных расходов, но это того стоит. Такой трюк с замыканием позволяет получить типобезопасную ссылку на свойство Name. В некоторых C# фреймворках этот подход принят и биндинги записываются именно в такой форме.

Неужто в Java мире всё настолько хуже?

Scala

В Scala пришлось изрядно попотеть. API макросов и квазицитат «quasiquotes» (да, они реально так безумно назвали свою фичу) является экспериментальным и в нём есть ряд ограничений. Первое — макрос и код с его использованием должны компилироваться независимо, их нельзя поместить в один Jar / модуль. Второе — IDE впадает в ступор и плачет горючими слезами при использовании квазицитат. Хотя Intellij Idea 14 вела себя терпимо.

Будем использовать вот такой макрос:

Здесь происходит примерно то же самое,  что в C# версии, за одним исключением. Код макроса исполняется во время компиляции и не требует больших накладных расходов. По сути макрос пытается разбить переданное замыкание на объявление типа и возвращаемое поле. Метод возвращает кортеж из класса и имени свойства.

Использовать очень просто:

Будущее уже здесь

На самом деле, разработчики языков программирования уже начали реализацию концепции nameof(). Такой оператор доступен в новой версии языка C# 6.0.

С нетерпением ждём языковой поддержки от Java и Scala!

Facebooktwittergoogle_plusredditlinkedin