Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Starling binding (http://www.flasher.ru/forum/showthread.php?t=206847)

Psijic 08.03.2014 20:40

Starling binding
 
Добрый вечер. Решил попытаться приобщиться на этом форуме к best practices. Ситуация следующая: автор Feathers на форуме сообщил, что сигналы теперь лучше не использовать поскольку события старлинга очень даже good. Я решил переписать для начала бинды флекса [Bindable]. Первоначальный вариант получился такой, что на каждую переменную пришлось делать гет-сет, которые раздули всю модель в несколько раз и из переменных + 3-4 функций в сумме около 100 строк (на всю модель) получилось строк 300. К тому же, сеттеры приходилось делать копипастой, что не слишком-то меня устроило. Решил сделать подкаст подкласс. Оцените реализацию и посоветуйте best practices.

Кусок модели-синглтона
Код AS3:

public class StateManager extends EventDispatcher
 
        private static var _instance: StateManager = new StateManager();
 
        public static function get instance(): StateManager
        {
            return _instance;
        }

Код AS3:

        public var _state: uint = STATE_LOADING;
 
        public function get state(): uint
        {
            return _state;
        }
 
        public function set state(value: uint): void
        {
            Bind.masterSetter(instance, "_state", value);
        }

Сама бинд-утилита

Код AS3:

    import starling.events.Event;
    import starling.events.EventDispatcher;
 
    public class Bind
    {
        public static const VARIABLE_CHANGE_ENDING: String = "_changed";
 
        public function Bind()
        {
            if (Bind)
                throw new Error("Bind is abstract.");
        }
 
        public static function masterSetter(host: EventDispatcher, varName: String, value: Object): void
        {
            if (host[varName] == value)
                return;
 
            host[varName] = value;
            trace(varName, value);
            host.dispatchEvent(new Event(varName + VARIABLE_CHANGE_ENDING));
        }
 
        public static function bindSetter(setter: Function, host: EventDispatcher, varName: String): void
        {
            host.addEventListener(varName + VARIABLE_CHANGE_ENDING, setter);
        }
 
        public static function bindProperty(site: Object, varName: String, host: EventDispatcher, chain: String): void
        {
            host.addEventListener(chain + VARIABLE_CHANGE_ENDING, bindChanged);
 
            function bindChanged(e: Event): void
            {
                site[varName] = host[chain];
            }
 
        }
 
    }

Вызов ака во Flex
Код AS3:

Bind.bindSetter(stateChanged, StateManager.instance, "state");

Код AS3:

Bind.bindProperty(io, "dataProvider", model, dataProvider);

Известные проблемы:
для использования функции masterSetter(тм) пришлось делать переменные public. Думал, обойдется хотя бы internal, но не вышло. Хотя, в feathers автор рекомендует для разработчиков фреймворка не делать private - переменных чтобы можно было легче наследоваться от компонентов, все-таки у меня это вроде как главная проблема.

Удобства:
Ну вообщем-то вроде как толковая замена стандартному бинду флекса без всяких изысков на событиях старлинга, которые реально стоит использовать в противовес стандартным флэшовым событиям.

Варианты:
Может, попробовать сделать Bind не как класс, а как отдельные функции через include чтобы переменные были private? Либо есть вариант в каком-то фреймворке делать BindInt, BindString etc.

Hauts 09.03.2014 08:16

Извините, что вмешиваюсь без конструктива, но объясните мне практическую ценность такого?

Psijic 09.03.2014 23:48

Цитата:

Сообщение от Hauts (Сообщение 1161346)
Извините, что вмешиваюсь без конструктива, но объясните мне практическую ценность такого?

Связь в системе MVC между классами Viev, Model, Controller.

Так, выяснилось, что текущий masterSetter работает не совсем корректно. По-моему, событие не считывается при изменении. Можно попробовать добавлять на stage, тогда и наследоваться от dispatchEvent не придется, хотя по-моему, этот метод хуже. Хотя, с хорошими сепаратором и парсилкой можно и так заделать.
Если вынести masterSetter как отдельную функцию, то переменные можно оставлять private. Вопрос с событиями.

Добавлено через 5 минут
Код AS3:

    public function masterSetter(host: EventDispatcher, varName: String, value: Object): void
    {
        if (this[Bind.VARIABLE_PRIVATE_PREFIX + varName] == value)
            return;
 
        this[Bind.VARIABLE_PRIVATE_PREFIX + varName] = value;
        host.dispatchEvent(new Event(varName + Bind.VARIABLE_CHANGE_ENDING));
    }


Psycho Tiger 10.03.2014 14:59

А если попробовать наследоваться, кажется, от Proxy и ловить undefined method?
Или сделать класс dynamic и метапрограммированием добавлять методы в рантайме?

Psijic 10.03.2014 15:40

Цитата:

Сообщение от Psycho Tiger (Сообщение 1161398)
А если попробовать наследоваться, кажется, от Proxy и ловить undefined method?
Или сделать класс dynamic и метапрограммированием добавлять методы в рантайме?

хм, вроде не сталкивался с таким, нужны пояснения.

Оказалось, события все-таки передаются и считываются правильно. Значение не обновляется. this - похоже, не прокатывает. Показывает global

silin 10.03.2014 17:26

разу оговорюсь, что я мало понимаю в этом,
но если единственной задачей этой конструкции будет синхронизация свойств, то я не вижу почему бы по-простому не создать словарь соответствий и менять свойства напрямую, без посредников (событий)

Код AS3:

package  
{
        import flash.utils.describeType;
        import flash.utils.Dictionary;
 
        public class Bind
        {
                public static var map:Dictionary = new Dictionary();
                public function Bind()
                {
                        throw("Bind is static");
                }
 
                public static function update(src:Object, prop:String):void
                {
 
                        if (map[src] && map[src][prop])
                        {
                                for each(var obj:Object in map[src][prop])
                                {
                                        obj.targ[obj.prop]= src[prop];
                                }
                        }
                }
 
                public static function bindProperty(src: Object, srcProp: String, targ: Object, targProp: String): void
                {
 
                        if (!map[src]) map[src] = {};
                        if (!map[src][srcProp]) map[src][srcProp] = [];
                        map[src][srcProp].push( { targ:targ, prop:targProp } );
 
                }
        }
 
}

Код AS3:

package 
{
        import flash.display.Sprite;
 
        public class Main extends Sprite
        {
                public var prop:String;
 
                public function Main():void
                {
 
                        var test:Test = new Test();
                        var test2:Object = { };
 
                        Bind.bindProperty(test, "prop", this, "prop");
                        Bind.bindProperty(test, "prop", test2, "prop");
 
                        test.prop = "newValue";
 
                        trace( "this.prop : " + this.prop );
                        trace( "test2.prop : " + test2.prop );
 
                }
 
        }
 
}

Код AS3:

package  
{
 
        public class Test
        {
                private var _prop:String = "xx";
 
                public function Test() {}
 
                public function get prop():String
                {
                        return _prop;
                }
 
                public function set prop(value:String):void
                {
                        if (_prop != value)
                        {
                                _prop = value;
                                Bind.update(this, "prop");
 
                        }
 
                }
 
        }
 
}


Psycho Tiger 11.03.2014 10:41

Код AS3:

dynamic class SexyModel extends Proxy implements IEventDispatcher{
        // todo: implement IEventDispatcher
    public function SexyModel() {
 
    }
 
    override flash_proxy function callProperty(methodName:*, ... args):* {
                // скажем, меняем свойство если обращаемся так: sexyModel.setCustomers(customers), sexyModel.setWaitFlag, т.е. метод начинается на 'set'
                // 1. проверяем подходит ли methodName под нашу маску (set*)
                // 2. меняем своё свойство. Например, если такого свойства у нас нет – лучше кинуть RTE или хотя бы warn самостоятельно, т.к. класс dynamic и VM за тебя это не сделает
                //        т.к. язык типизированный, идиологически придётся сравнивать даже типы вручную.
                // 3. диспатчим правильный эвент
    }
 
    override flash_proxy function getProperty(name:*):* {
                // или вариант, что все свойства к модели будут обращаться через [], типа sexyModel[sexyProperty] = 'sexyValue'
    }
 
    override flash_proxy function setProperty(name:*, value:*):void {
        // см. выше
    }
}

Возможные проблемы:
1) "свои" методы модели, возможно, придётся разруливать через тот же callProperty. Вероятно любое обращение к свойствам идёт через это callProperty, поэтому это накладывает свои ограничения, но они решаемы. Я точно не знаю, на as давненько не писал ничего серьезного.
2) Скорее всего это медленно. Чаще всего это не будет никого волновать, но для "узких" мест может потребоваться "настоящий" инлайн-код и прочий тектоник вокруг этой модели. Use wisely.

Psijic 11.03.2014 13:42

silin
я так понял, у вас реализован Observer? Я сначала тоже думал сделать через сигналы, а потом нашел вот ту статью, что я упоминал в заголовке.
Psycho Tiger
на вид что-то слишком неочевидно.

Цитата:

2) Скорее всего это медленно. Чаще всего это не будет никого волновать.
это все-таки важно )). Лучше уж тогда вручную прописывать каждый сеттер либо обсервер.

Добавлено через 33 минуты
А можно как-нибудь подключить функцию без пакета? Пробовал вынести

Код AS3:

function masterSetter1(host: EventDispatcher, varName: String, value: Object): void
{
    if (host[Bind.VARIABLE_PRIVATE_PREFIX + varName] == value)
        return;
 
    host[Bind.VARIABLE_PRIVATE_PREFIX + varName] = value;
    host[varName] = value;
 
    host.dispatchEvent(new Event(varName + Bind.VARIABLE_CHANGE_ENDING));
}

Даже подключил файл с ней
Код AS3:

include '../../../masterSetter.as';

но саму функцию не смог

silin 11.03.2014 15:01

>>у вас реализован Observer?..
ничего этого не понимаю, но городить систему событий со связыванием пар свойств через замыкание в анонимном листенере ну как-то очень уж затейливо на мой взгляд при том, что ничего не мешает сделать это напрямую
событийная модель старлинга\физерса, как и предпочтения Tynjala, тоже не понятно при чем здесь

Psijic 11.03.2014 15:24

Цитата:

Сообщение от silin (Сообщение 1161495)
>>у вас реализован Observer?..
ничего этого не понимаю, но городить систему событий со связыванием пар свойств через замыкание в анонимном листенере ну как-то очень уж затейливо на мой взгляд при том, что ничего не мешает сделать это напрямую
событийная модель старлинга\физерса, как и предпочтения Tynjala, тоже не понятно при чем здесь

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

P.S. Хорошая реализация Observera, прямо под имена функций биндинга. Надо бы заценить на тесте.


Часовой пояс GMT +4, время: 03:12.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.