Доброго времени суток, %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
Отдельная благодарность Александру Козловскому, Харват Егору, Сергею Драган и Сергею Мирянову за помощь в написании статьи.