Под впечатлением от крайней конференции я сделал для себя массу выводов. Одним из которых и станет эта статья. Речь в статье пойдет о библиотеке bindx.
Но для начала или наоборот по прочтению статьи советую посмотреть мою презентацию на конференции об этой же библиотеке:
Data binding
Если снова обратиться к википедии, а именно к определению понятия Data binding, то мы сразу узнаем одну из основных задач, для чего биндинги могут быть использованны - связь GUI с моделью.
Вообще data binding можно и стоит переводить как связывание данных, а не называть заморским и не всегда понятным словом - биндинги. Из понятия “связывание данных” понятно, что мы связываем что-то, а связь подразумевает, что у нас есть как минимум два операнда, которых мы собственно и связываем. Но хватит ванговать, смотрим картинку:
Кто пошел по ссылке выше на msdn, тот скорее всего ее уже нашел. Видно что у нас есть источник и целевой объект, источних предоставляет нам данные, которые мы и передаем приемнику. Но в отличии от простой передачи свойсва, тут происходит именно связывание (поэтому двухстронняя стрелочка, хоть данные бегают лишь в одну сторону). Связывание данных позволяет целевому объекту быть всегда вкурсе о значении свойства и своевременное оповещает его об этом изменении.
На самом деле связований бывает несколько видов, не говоря уже о самих реализация, но все это можно узнать из просторов интернета. Теперь, когда мы хоть примерно знаем как должно работать связывание, подумаем, где бы мы могли это использовать в Haxe разработке. Но давайте рассмортим применение внимательнее, а уже потом вернемся к самой либе.
Применение
Чтобы не придумывать, я и AxGord лишь расскажем, где реально мы это используем: (а в дальшейшем надеюсь не только мы)
Привязка логики модели к визуальной части. Самое частое и простое вывод текста. Используется как для Unity так и для Flash платформы.
Все описанные выше примеры - это лишь малая часть, где можно использовать связывание данных. Но даже их, должно хватить, чтобы заинтересовать вас.
bindx (bindx.IBindable)
Вернемся к нашим баранам и рассмотрим основные возможности, которые предоставляет наша библиотека bindx.
Для начала вам надо знать, что все что вам надо - это интерфейс bindx.IBindable
и утилитарный класс bindx.Bind
, который чаще всего вы будете использовать через using bindx.Bind
. Ну еще не стоит забывать про мета данные, которые помечают свойства для связывания.
Рассмотрим простой пример класса реализующего интерфейс IBindable
С первой же строчки мы видим мету @bindable
перед определением класса, она не обязательная, но ее наличие говорит библиотеке, о том чтобы всем публичным переменным с сеттером default
или set
автоматически добавлялась мета @bindable
. Это очень удобно и позволяет сильно сократить кол-во набираемых символов. В нашем примере переменная a
получит мету @bindable
, т.к. соответствует всем условиям.
Значит еще раз, @bindable
перед классом автоматически добавит мету @bindable
всем
Далее видно, что у свойства b
заданные дополнительные параметры в мете. На сегодняшний день поддерживаются два дополнительных свойства:
force
(по умолчанию false
) - в случае true не смотрит на сеттер и никак его не меняет (об этом подробнее ниже). Force флаг очень нужен в случае когда сеттер указан как never, null или dynamicinlineSetter
(по умолчанию true
) - в случае генерации сеттера для свойства этот флаг отвечает, будет ли сеттер заинлайнен или нет.Теперь когда мы немного разобрались с мета данными, самое время разобраться, зачем же мы их так тщательно расставили. Дело в том, что все @bindable переменные или почти все, подвергаются сильным изменениям, а именно для default
сеттера создается сеттер функция и переменные типа var a:Int;
становятся переменными типа var a(default, set):Int,
а в случае если сеттер уже существовал, то тело функции сеттера немного изменится. Сделано это все, для того, чтобы оповещать остальных, об изменениях @bindable
свойств.
Рассмотрим отдельно два случая:
var a:Int;
Inline метод будет если не указать дополнительный флаг inlineSetter = false
.
var b(default, set):Int;
тогда измененный сеттер с поддержкой связывания данных будет выглядеть так:
Первое, что бросается в глаза, это проверка с предыдущим значением, это необходимо, чтобы не было ложных срабатываний сеттера и в большинстве случаев никак не повредит вашему сеттеру, а даже напротив, позволяет вам не делать такую проверку самому (для остальных случаев, когда такая проверка только мешает есть флаг force
(см. выше)). Ну и второе изменение сеттера, это диспатч изменения прямо перед самым ретурном. И не стоит боятся использовать сложные конструкции в ретурнах, типа return this.a = value
; библиотека отлично обработает их, правда изменив на боле сложную конструкцию:
Это нужно знать и учитывать, особенно если у переменной a
есть геттер и вам не хочется, чтобы он срабатывал несколько раз за один сеттер.
Теперь, когда вы точно знаете, что на самом деле сделает библиотека с @bindable
свойствами, я могу спать спокойно. Ну т.е. пора переходить к загадочному __fieldsBindings__
. На самом деле таких загадочных свойства у всех IBindable
классов целых два. Они даже прямо прописанны в интерфейсе:
Но определять их в классах не обязательно, я бы даже рекомендовал никогда не делать этого, оставив и это грязное дело библиотеке. Все что надо от вас, то это обязательно определять конструктор во всех IBindable
классах. Это нужно, чтобы встроить в него инициализаторы сигналов.
Но вернемся к нашим сигналам, __fieldBindings__
оповещают в случае, если хоть одно свойство изменилось, __methodBindings__
- в случае методов (про биндинг методов смотрите в моем видео). Но ни те ни другие в 99% случаев вам напрямую использовать не понадобится и я снова рекомендовал бы вам не трогать их никогда, разве что в деструкторе, в котором можно вызвать destroy()
метод у обоих сигналов, чтобы наверняка отписаться от всех слушателей и позволить сборщику мусора сделать свое грязное дело с легкостью. Самые любознательные могут изучить код самих сигналов и может даже оптимизировать их, т.к. их скорость это самое узкое место библиотеки.
Уже тут у вас должно были сложиться вырожение лица аналогичное смайлу 0_o, по крайней мере именно к этому эффекту стремиться библиотека bindx. И вы просто не поверите, но впереди еще масса интересного. Интересного еще настолько много, что представить это в данной статье не вижу возможным. Поэтому на этом мы пока остановимся, но это остановка на полуслове и скоро будет продолжение. А пока небольшие выводы.
Вывод
В данной статье мы немного узнали о связывании данных, мы почти полностью узнали как работает макрос (а-а-а, я впервые за всю статью использовал это слово, а ведь на самом деле 90% работы делают в либе именно макросы), который автоматически вызывается в случае реализации bindx.IBindable
интерфейса. Мы узнали некоторые важные обстоятельства поведения биндингов, которых нет даже в документации (а все потому что, документации тоже нет). Если вернуться к нашей первой картинке, то видно, что мы изучили то как bindx подготавливает источник привязки.
Что нам осталось? Осталось изучить как нам к этому источнику привязываться. Об этом в следующей статье.
P.S. Установить библиотеку можно и через haxelib:
Отдельная благодарность AxGord-у, SlavaRa, Харват Егору и Бурдун Александру за помощь в написании статьи.