Доброго времени суток, %haxe_user%! Эта статью, с определенным допущением, можно назвать долгожданным продолжением первой статьи о связавании данных в хаксе.
С выхода библиотеки bindx прошло много времени и пришло время доработать, а точнее даже переработать всю библиотеку. Изменений настолько много и они настолько глобальные, что решил написать подробную статью. Отсюда и такое название самой статьи. Но обо всем по порядку.
Первое, что сразу бросится в глаза - это новая система сигналов. Если раньше для всех полей создавался единый диспатчер, то теперь создается сигнал для каждого поля. Например для поля
fooChanged
, на который можно сразу подписаться:Bind.bindx
, хотя они и остались.
Но и тут надо зайти издалека. Все началось с того, что меня попросили поддержать стороние сигналы в библиотеке, что и было реализованно в новой версии. В этом есть смысл и поэтому появился bindx.IBindingSignalProvider,
реализовать который не так уж просто, но зато у вас могут появиться данные связанные с вашими сигналами и дженериками. Из коробки поставляются максимально простые сигналы, но для 99% случаев их должно хватить. Базовые сигналы поддерживают несколько важных настроек:
lazySignal
- ленивая инициализация сигналов. Сигналы создаются при первом обращении к геттеру сигнала (по умолчанию включена). Выглядит это примерно так:inlineSignalGetter
- добавление inline геттеру сигнала. Актуально только в случае включенной lazySignal
настройки (по умолчанию отключено).Итого у нас есть два способа инициализации сигналов и в случае создания геттера сигнала, мы можем сделать его инлайновым. Если у вас будут и другие пожелания, я готов их обсудить и по возможности реализовать.
Теперь, когда я немного рассказал про "поставщика сигналов" и базовые сигналы, стоит объяснить зачем остались Bind.bindx. Дело в том, что код вида fooChanged.add
“сигнало зависим”, и при смене сигналов он возможно перестанет работать, в то время как Bind.bindx
не поменяет своего апи и учитывает lazySignal
параметр. Что использовать - решать вам. Главное, что я вас предупредил :). Не стоит забывать и о Bind.unbindx
имеющем обратный эффект, но при этом таком же полезном.
Еще один очень полезный метод Bind.bindTo
, позволяющем связывать одно свойство с другим и возвращающем метод для отписки:
Подписка на методы выполняется аналогично, а оповещать об изменениях методов можно напрямую, вызывая метод dispatch у сигнала или используя утилитарный класс bindx.Bind.notify(this.toString)
. И снова важно понимать, что если вы вызовете напрямую dispatch
у сигнала, то в случае ленивой инициализации сигнала, вы автоматически его создадите, в то время, когда notify все это учитывает. Диспатчить напрямую сигналы я вообще не советовал бы, но, зная программистов, даже не стал делать метод приватным - не поможет. Bind.notify
умеет оповещать об измененниях и обычных свойств:
Если вы еще не открывали первую статью про bindx первой версии, то самое время это сделать (Bindx intro). Потому что далее пойдет речь об интерфейсе bindx.IBindable, а его поведение во многом сохранилось. Он все также для всех переменных создает приватные сеттеры с диспатчингом об изменении. Он все так же патчит уже существующие сеттеры, добавляя перед самым возвратом функции вызов сигнала, а в начале метода дополнительную проверку на то, что новое значение действительно отличается от предыдущего (если что-то из этого вам только мешает, используйте force параметр).
@:bindable мета поддерживает два дополнительных параметра:
force
- не будет модифицироваться сеттер и не создаст никаких новых сеттеров, появится только сигнал. А вот оповещение об изменении полностью на вас. Очень полезное свойство, позволяет полностью управлять оповещением об изменении свойства и предотвратить автоматическую модификацию сеттера (по умолчанию отключено).inlineSetter
- актуально только для переменных без явно указанного сеттера (var foo:Bar), автосгенерированный сеттер будет инлайн (по умолчание отключено).Стоит напомнить и правила @:bindable
метаданных. Что подвергнется обработке:
@:bindable
(кроме конструктора и статических),@:bindable
. Тут очень важно указать, что все атрибуты указанные в мете класса, автоматически скопируются всем полям из данной группы. Например указав классу @:bindable(inlineSignalGetter=true)
вы автоматом включите это свойство всем полям у которых нет своей меты @:bindable
. Такое себе наследование, правда не передается дочерним классам.
Параметр force уменьшает требования для полей и позволяет связывать данные даже без сеттера (то есть один из вариантов: never, null, dynamic
).
Классы реализующие интерфейс IBindable
можно наследовать - все дочерние классы будут отлично работать и также поддерживать мету @:bindable
.
Напоследок небольшой пример для наглядности с пояснениями.
Ну а теперь, немного разберем сам пример:
Переменная foo
обзаведется инлайновым сеттером inlineSetter=true
. Геттер сигнала не будет инлайновым inlineSignalGetter=false
, но в нем будет инициирован сигнал при первой необходимости lazySignal=true
. Как lazySignal=true
так и inlineSignalGetter=false
можно было не указывать, так как это их поведение по умолчанию.
Сеттер переменной bar
вообще не будет модифицироваться force=true
, лишь появится сигнал с ленивой инициализацией. Именно по этому мы вручную вызываем Bind.notify(this.bar…)
внутри сеттера bar
.
Ну а дальше можно просто подписываться на сигналы или, опять-таки, использовать bindx.Bind
:
На данный момент библиотека все еще развивается, и хочется получить как можно больше фидбека. Если вы использовали предыдущую версию bindx или вот-вот собирались это сделать, советую попробовать новую библиотеку. Установить ее можно с помощью команды:
haxelib git bindx2 https://github.com/profelis/bindx2.git
Отдельная благодарность Александру Козловскому, Харват Егору, Сергею Драган и Сергею Мирянову за помощь в написании статьи.