PDA

Просмотр полной версии : Должен ли вид быть IEventDispatcher'ом?


wvxvw
02.12.2009, 14:40
Мы тут недавно поспорили по этому поводу, но, хотелось бы услышать еще мнения.
Я думаю, что нет, другая сторона думаeт, что да.
Мои доводы:
Примеры классы Graphics и BitmapData, которые вполне подпадают под описание "вида" по моему мнению. + другие "самодельные" виды, типа как например управление HTML элементами страницы, или запись файла, который тоже может быть видом для другого приложения.
(Все, опять же только мое мнение).

Доводы другой стороны:
Флеш на 99% рассчитан на работу с дисплей объектами, и подписываться на события от них - это как бы в порядке вещей, и вообще удобно. + возможность диспатчить в них события типа "бродакаст" делает подход к проблеме несколько более универсальным. + одинаковый подход к работе с видом, т.как любой вид можно представить, как один и тот же интерфейс, не вдаваясь в конкретику. + это несколько упрощает планирование. Так, например, если апдейты во вью не моментальные, то мы легко решаем проблему асинхронности апдейтов.

Вобщем, хотелось бы услышать ваши мнения.

SamFR
02.12.2009, 15:27
Мне кажется, что, если мы говорим всё-таки о флеше, то здесь как минимум 90% видов наследуют от класса, реализующего этот интерфейс. Поэтому поводом к неиспользованию IEventDispatcher могут стать только существенные недостатки, которые это использование за собой повлечёт. Какие преимущества даёт использование этого интерфейса, вы только что подробно рассказали :) Серьёзных недостатков я не вижу. Да, идеологически немного неправильно, но только немного, и проявляться это будет нечасто. Так что, думаю, должен (вернее, стоит).

gloomyBrain
02.12.2009, 16:49
Я думаю да, конечно.
Любой вид должен имплементить этот интерфейс
идеологически немного неправильно
Не согласен. Если вы что-то видите, вы должны уметь это "потрогать".

bicubic_bublic
02.12.2009, 17:25
не соглашусь что BitmapData подпадает под описание "вида". это объект с данными, о чем нам и сигнализирует суффикс Data.

Graphics же - просто интерфейс (тоже не "вид")

divinus
02.12.2009, 18:34
я не думаю что это критично (Должен ли вид быть IEventDispatcher'ом), так как вид главным образом должен служить для отображения данных и если от него не требуется обратной связи (например регистрация действий пользователя) то нафига спрашивается ему быть диспатчером?
wvxvw, я за тебя :)

wvxvw
02.12.2009, 19:57
Просто когда мы это примеряем на флеш, то нам подход с дисплей объектами кажется очевидным, но, флеш это все таки не единственое место, где MVC имеет место быть :), в том же PHP существует точно такая же парадигма, и там как-то никто не предполагает, что вид будет дисатчить события обратно... Как правило предполагается в него просто сеттить чего-нибудь.

Да, может быть Graphics не самый удачный пример, птотому что он не расширяемый, а вот BitmapData вполне, как на мой взгляд может выступать в качестве вида - банальный пример:
Рисуем графику в битмапдату и передаем ее серверу на сохранение. В этом случае нашим видом будет битмапдата... Как бы интерфейс он же не обязан быть с пользователем, он может быть и с другой программой.

Gaen
02.12.2009, 22:33
А через что кроме вида можно получать события от пользователя? Лишив вид возможности рассылать события, мы лишаем приложение всякой интерактивности.
Если же вид является просто отображением, а не интерфейсом, то тогда он вполне может обойтись без рассылки событий.

mayakwd
02.12.2009, 22:42
GAIKER верно высказался, я тоже не совсем понял идею лишать вид диспетчеризации.
А броадкастинг очень плохая затея. Пользуясь броадкастингом можно запутать все связи между составляющими.
Каждый объект должен знать свое место и не знать ничего о тех к кому он не имеет отношения.

BlooDHounD
02.12.2009, 23:23
wvxvw, что-то не вяжется твоя теория о том, что BitmapData - это представление. в твоей схеме представлением является сервер :) контроллер заполнил модель ( BitmapData ) и отдал представлению ( сервер ).

а по поводу IEventDispatcher... необязательно представление должно его реализовывать. просто в АС3 у тебя не получится иначе :) зато в том же пыхе, у тебя это получается. хотя если результатом работы скрипта является HTML, то в итоге ты получишь IEventDispatcher только браузеровский.

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

wvxvw
02.12.2009, 23:40
Ну битмапдата, имелась в виду, например, такая ситуация:
У нас есть русунок, есть его представление в векторном виде - это бишь модель. И есть его представление в виде пиксельной картинки. Эта картинка вообще не является частью интерфейса, например, карта, куда пользователь кликал за время пребывания на сайте. С точки зрения сервера картинку можно рассматривать как модель, но на клиенте это все таки будет вид ИМО.

И это я не против наличия свойств диспетчера в виде, я против того, чтобы, например контроллер вcегда предполагал, что вид является диспатчером. Т.е. должен ли контроллер:
// достаточно ли делать так
view.dispatchEvent(new Event("update"));
// или все-таки так
if (view is IEventDispatcher) view.dispatchEvent(new Event("update"));
else view.update();

BlooDHounD
03.12.2009, 00:10
У нас есть ... представление ... - это бишь модельнеее ) давай не будем натягивать мифические абстракции. рисунок это представление, на основании которого ты по событию можешь сделать ещё одну модель при помощи контроллера.И это я не против наличия свойств диспетчера в виде, я против того, чтобы, например контроллер вcегда предполагал, что вид является диспатчером.прочитай ещё раз последний абзац (http://www.flasher.ru/forum/showpost.php?p=869884&postcount=9). а что касается кода, то он вообще не верен. никто не должен диспатчить чужие события. и уж тем более контроллер, который это событие ловить и должен по логике )

Gaen
03.12.2009, 01:25
view.dispatchEvent...
А в каких случаях вообще требуется извне заставлять view запустить событие?
В данном случае скорее логично что представление получило событие от модели, обновилось и само послало об этом событие.

wvxvw
03.12.2009, 02:14
А в каких случаях вообще требуется извне заставлять view запустить событие?

А то, что это случается на каждом шагу в дисплей листе флеш плеера - это вас как-то не смутило, да? (Blood, это и тебе ответ).
А каким образом по вашему происходят т.н. broadcast type events? Ну, например, Event.ADDED_TO_STAGE, Event.ENTER_FRAME и тому подобные? Или вы каким-то образом подпишитесь в виде (он же, пусть Sprite) на события от контроллера (он же Stage) до получения ссылки на контроллер? Как позвольте спросить?

etc
03.12.2009, 09:11
wvxvw, это механика работы плеера, сам программер получает событие от самого чилда.

Но вот принудительно вызывать методы типа update и слать события извне — не комильфо. У нас, например, вообще снаружи отправлять события нельзя.

Котяра
03.12.2009, 10:26
обычно у меня происходит нечто вроде этого:
addModelViewer(view1,model1);
view1.addEventListener("button_click", funk1);
addModelViewer(view2,model1);
если view2 не диспатчит события, то на них и не подписываемся. хотя в моей реализации IView расширяет IEventDispatcher

alexcon314
03.12.2009, 10:48
Для многопоточной среды ваш спор имел бы смысл, но не принципиальный. Можно так, можно этак, договоримся. Если и не договоримся, плевать, я лично долго не спорил бы)). Там хоть есть, о чем поговорить.
Плеер - однопоточная среда, все эти игрища с эвентами - сплошное запудривание мозга.
Диспатчить чужие события не хорошо, твой аргумент это подтверждение/следствие опять же однопоточности. Точнее, эти эвенты как раз и завязаны на общий поток исполнения.
В частности, я не понял, как наличие диспатчера в представлении поможет решить проблему асинхронности апдейта.

Нет, ну можно поговорить о распределенных приложениях с участим плеера, типа клинет-сервер, несколько флэшек на странице, оболочка. В этом случае сервер/другая флэшка/оболочка -часть общей архитектуры приложения, и в таких системах чаще плеер сам как вид)).

bicubic_bublic
03.12.2009, 11:24
а однопоточная ли плеер среда? работал с Socket и наткнулся на то что иногда приходит новый пакет пока еще обрабатывается старый и параллельно начинает обрабатываться..

не исключено правда, что я где-то накосячил.. но все таки у меня появилось ощущение многопоточности плеера..

Котяра
03.12.2009, 11:41
AS код - однопоточен, но внешние операции (прорисовка, загрузка внешних данных, слушание мыши и пр..) Может и идут в других потоках. Хотя не факт.

wvxvw
03.12.2009, 13:28
Диспатчить чужие события не хорошо, твой аргумент это подтверждение/следствие опять же однопоточности. Точнее, эти эвенты как раз и завязаны на общий поток исполнения.
В частности, я не понял, как наличие диспатчера в представлении поможет решить проблему асинхронности апдейта.

wvxvw, это механика работы плеера, сам программер получает событие от самого чилда.

Но вот принудительно вызывать методы типа update и слать события извне — не комильфо. У нас, например, вообще снаружи отправлять события нельзя.

А почему плохо диспатчить чужие события? Я как-то не могу придумать ситуацию, когда это чем-то навредит. Если бы было плохо - dispatchEvent() был бы protected а не public...
И как бы события они скорее следствие того, что плеер, как програма - многопоточный, а язык не предусматривает такой возможности ИМО.

Gaen
03.12.2009, 13:41
Объект диспатчит событие, когда хочет сообщить о чем-то, что произошло с ним или внутри него. Это понятно, логично и правильно.
А вот когда кто-то начинает управлять отсылкой событий через подставное лицо, сразу возникает куча вопросов. И первый из них - если что-то произошло в объекте А, то почему вдруг я узнаю это от Б?

wvxvw
03.12.2009, 14:19
Тоесть пример с ентерфреймом вас не убедил? :)
Ну хорошо, такой пример:
У нас есть соединение с сервером, которое посылает команды клиенту.
Мы можем использовать следующие подходы:
Самый простой:
У класса соединения определяем все методы, которые можно вызвать.
- Недостатки, соответственно, отсутствие гибкости, возможно куча методов, многие из которых не будут вызываться никогда в некоторых сценариях и т.д.
У класса соединения есть свойство client и этот объект уже должен реализовывать методы, которые мы собираемся вызывать через удаленную процедуру.
- Недостатки: нужно создавать какие-то служебные объекты и, почти наверняка некоторые методы прийдется реализовывать в каждом из клиентов, или создавать целую иерархию таких клиентов с абстрактными классами... вобщем, мрак и куча лишнего кода...
Мы можем создать универсальный обработчик, в который мы будем передавать название или ссылку на нужный нам метод клиента + аргументы и надеятся на то, что обработчик их вызовет через отражение или еще как.
- Недостатки: клиент может не обработать сообщение вообще.

Изначально предполагается, что ссылку на класс "раздающий указания" мы не хотим или не можем по техническим причинам передать в классы которые слушают / выполняют указания. Потому что, например, получение такой ссылки повлечет за собой создание ненужных зависимостей.
Пример: Stage попадает в список зависимостей DisplayObject потому, что у DisplayObject есть свойство stage, но мы бы могли это избежать просто работая с событиями, которые нам stage диспатчил бы от имени получателя (таким образом не давая ссылку на самого себя).

alexcon314
03.12.2009, 15:08
Что-то я не пойму твою мысль..
Значит, нам приспичило иметь объект А, который может диспатчить события, но от имени другого объекта В. Так. Хоршо.
Этот другой объект В ничего не знает и не дожен знать о существовании объекта А. Так.
Мы имеем таким образом, независимый от А В. Так. Тогда А получается зависимым от В, ибо нужна ссыль чтоб диспатчить. Так.
Не понял.
По-другому.
Имеем В, который диспатчит эвент. Так. Имеем А, которму пофик на всех. Так.
Но, что бы получить эвент, надо подписаться, значит А должен иметь ссыль на В.
Стало быть А, будь он сторонним рассыльщиком, или просто листенером должен иметь ссыль на В. Не понял.
Хорошо. Пусть есть С, который хочет получать эвенты от А. Но мы хитро диспатчим через В, ибо не хотим, чтобы С, знал про А. Так. Тогда имеем С с сылкой на В, и А с сылкой на В. С и А ничего не знают друг о друге. Так. Но А, тем не менее шлет эвенты С, правда С получает их от имени В, но тем не менее. Круто.
Так что ли?
А в чем должен был убедить пример с энтерфреймом? В том что так бывает, и даже неплохо работает?
значит А - это стейдж, В - это объект, у которого А "поддиспатчивает" энтерфрейм. Так. Значит, мы можем иметь С, который получает энтерфрейм от стейджа, и который о стейдже ничего не знает.
Так что ли?

BlooDHounD
03.12.2009, 15:15
wvxvw, что в примере с энтерфрэймом могло меня убедить, и в чём?
я вообще не понимаю твои примеры. как в анекдоте:ВасильИваныч: - Петька, приборы!
Петька: - ВасильИваныч!, 36!
ВасильИваныч: - Что 36?
Петька: - А что приборы?enterFrame диспатчик сам объект. просто через презерватив. что-то типа этого:public override function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
super.addEventListener( type, listener, useCapture, priority, useWeakReference );
switch ( type ) {
case Event.ENTER_FRAME:
case Event.EXIT_FRAME:
case Event.FRAME_CONSTRUCTED:
// case: и остальные кейсы
_BROADCASTER.addEventListener( type, super.dispatchEvent, useCapture, priority, useWeakReference );
break;
}
}я практически уверен, что бродкасер там не собирает все дисплэйобджекты, что бы пробежаться по массиву и вызывать dispatchEvent.

и с передачей всех дисплэйобджектов бродкастеру, ссылок там ещё больше получается.

wvxvw
03.12.2009, 16:10
alexcon314:
Да, практически так. Но немного запутано...
Есть А и Б, А создал Б и после его создания проделал еще какие-то свои операции и продиспатчил в Б собыия сигнализирующие о том, что эти операции были сделаны.
пример:
DispalyObjectContainer#addChild(child);
Наши варианты действий:
Мы можем у child вызвать метод onAdded() а можем проверить а не подписался ли child на "onAdded" и только в случае если подписался вызвать onAdded() о сыществовании которого мы можем до этого и не знать вообще. Второй вариант экономичнее и не обязывает child имплементить onAdded в обязательном порядке.
На это накладывается еще такой момент: мы не хотим позволять ребенку знать больше чем положено о его родителе, т.как не хотим давать ему какие-либо полномочия по управлению родителем. По этому мы продиспатчим в него событие от имени ребенка, ну или не важно, пусть даже target будет null, главное ссылку на родителя не давать.
Если вы задумаетесь о таком подходе к, например, stage - то это бы разом решило кучу непоняток с песочницами, неудаляемым загруженым контентом и т.п. Т.как в таком случае у ребенка не было бы ссылки на stage, и он бы не смог создать жесткие ссылки туда, куда ему лезть не разрешено...

BlooDHounD:
Я почему-то думаю, что там схема такая же, как и в IE6 (window.event). Т.е. имеется какой-то глобальный броадкастер к которому мы подписываемся когда подписываемся на тот же ентерфрейм. Почему я так думаю - потому что mx EventDispatcher был именно так организован, и я сомневаюсь, что схему переделали. И этот самый глобальный броадкастер именно диспатчит события от имени подписчика. Иначе мне тяжело представить, каким образом ентерфрейм наступал бы синхронно везде, да и вообще, откуда бы клип узнал, что ему нужно продиспатчить ентерфрейм?

ЗЫ. Анекдот был в оригинале про летчиков :)

Gaen
03.12.2009, 16:28
этот самый глобальный броадкастер именно диспатчит события от имени подписчика. Иначе мне тяжело представить, каким образом ентерфрейм наступал бы синхронно везде, да и вообще, откуда бы клип узнал, что ему нужно продиспатчить ентерфрейм?

Все это - внутренние механизмы плеера, у них свои концепции и свои косяки. Это тот black box, внутреннее устройство которого нас не интересует на уровне приложения, работающего под плеером.

BlooDHounD
03.12.2009, 17:14
wvxvw, не представляешь? так я же код написал ) событие срабатывает у всех синхронно согласно моему коду. есть глобальный диспатчер, который является статическим экземпляром ( _BROADCASTER ). при этом писать супер навороченный функционал по обработки ВСЕХ дисплэйобджектов не нужно. даже не нужно собирать их в коллекцию. достаточно подписать на событие у этого брадскасера и послать его от своего имени. в твоём же случаи надо написать тонну кода, который работает медленнее, так как он пробегает ВСЕХ детей и проверяет, а есть ли у них слушатели? в моей схеме в коллекции собираются только объекты, у которых есть слушатели. и собираются они готовым функционалом EventDispatcher'а, что с точки зрения здравого смысла и экономичнее и быстрее, а с точки зрения ООП, ваще идеально вписывается ( переиспользование инкапсулированного класса ).


твой пример с addChild мне опять не понятен. если ребёнку не нужно знать, что его добавили куда-то, то зачем в вообще заморачиваться с onAdded? и скорее всего не вызывается в плэйере такого метода. как мне кажется там вызывается метод internal function setParent(parent:DisplayObjectContainer):void;, что опять же логичнее. ребёнок сравнивает "а тот ли у него папа?". если тот же, то return. если новый, то диспатч removed, сохранили папу, и следом сразу диспатч added. все довольны. если надо, то конечно мы сперва проверим подписчиков, но этот не тот случай, так как тут bubbling. внутри метода setParent, мы ещё сражу вызовем метод setStage, в котором произведём аналогичную операцию.

события диспатчатся от имени child, почему о них должен заботится родитель? не понятно. так же непонятно, зачем родителю совершать ненужные и идиотские проверки, если ребёнок не должен диспатчить это событие ни в коем случаи? в моей схеме понятно. отнаследовались и переопределили setParent. а в твоей? закидываем дрожжей в родительский класс и начинаем плодить switch/case на каждое особенное поведение ребёнка в одном методе?

и самое обидное, что этот код вообще может не понадобится, так как я не использую никого кроме одного конкретного ребёнка. зато всt логические деревья детей вкомпилятся в приложение в обязательном порядке, так как поведение детей описывается в классе, в котором вписан switch и 15 case, в которых надо информация о идентификации детей.

резюме: повторяю в 3й раз, что то, что ты описываешь ни коем образом не касается имплементации EventDispatcher. это вопрос из области "а использовать ли мне ООП"?

wvxvw
03.12.2009, 17:49
Blood, твой код не отражает действительность - таргет события полученого в обработчике ентерфрейма не будет == _BROADCASTER... я не спорю о достоинствах такой схемы, просто в реальности она так не работает.

Как раз если бы ты прочитал внимательнее, ты бы понял, что никакого switch-case не предвидится. Есть набор предопределенных событий которые может продиспатчить родитель. И тип ребенка не влияет на логику - главное, чтобы ребенок был IEventDispatcher.
По поводу:
internal function setParent(parent:DisplayObjectContainer):void;
Как раз, опять же, если ты прочитаешь внимательнее, то увидишь, что мне как раз не нужно давать ссылку на родителя, ни явно ни в обход ни вообще никак, - мне нужно это избежать... Мне нужно передать событие сигналящее о том, что, например, нужно обновить внешний вид... Как пример - ситуация со стейдж - каждый кому не лень вдруг может сеттить displayMode / align? И по-твоему, это как бы хорошо и в порядке вещей?

BlooDHounD
03.12.2009, 19:03
кароче. ты видишь то, что хочешь видеть и чёрти знает что придумываешь. да, target !== _BROADCASTER, так как именно это нам и надо! ты вот сейчас о чём? нам же надо что бы таргет был child.

что касается всего остально, я уже описал в чём минусы схемы. она не ООП. то есть она не расширяема. наважно, что у тебя там будет. этот вопрос вообще не относится к событиям это вопрос архитектуры. если не понимаешь, то забудь. у тебя вопрос не правильно задан.

Как раз если бы ты прочитал внимательнее, ты бы понял, что никакого switch-case не предвидится. Есть набор предопределенных событий которые может продиспатчить родитель. И тип ребенка не влияет на логику - главное, чтобы ребенок был IEventDispatcher.я всё очень внимательно прочитал. может быть даже слишком внимательно, и поэтому ты не понимаешь теперь, что события тут не причём. ребёнок IEventDispatcher, значит есть метод dispatchEvent. а ты паришь мозги про детей не IEventDispatcher. у которых хочешь впихивать какие-то методы onAdded(), которых может и не быть. то есть к интерфейсу IEventDispatcher они отношения не имеют.Мне нужно передать событие сигналящее о том, что, например, нужно обновить внешний вид...во флэше это сделано как-то так:stage.invalidate();внутри инвалидэйт чего-то там проверяется и диспатчится событие в том случаи, если это надо, а не на каждый вызов.

Котяра
04.12.2009, 12:36
вот как может выглядить код в базовом EventDispather
public function addEventListener(evenType:String, listener:Function):void
{
if (evenType==Event.ENTER_FRAME)
globalEnterFrameDispatсher.addEventListener(Event.ENTER_FRAME,dispatchEnterFrameEvent);
//дальше обычный ф-ционал
}
public function dispatchEnterFrameEvent(event:Event){
// что-то вроде этого
event.currentTarget = this;
event.target = this;
// или просто создаем новый энтерфрэйм эвент
dispatchEvent(event));
}
В этом случае globalEnterFrameDispatсher рассылает события только тем объектам у которых есть слушатели.

BlooDHounD
05.12.2009, 16:43
Котяра, чем Ваш код отличается от моего? не считая дополнительных телодвижений, и дополнительной функции?

Котяра
07.12.2009, 10:17
Котяра, чем Ваш код отличается от моего? не считая дополнительных телодвижений, и дополнительной функции?
Каюсь.. ваш код я как-то пропустил мимо..
Если отличий мало, значит это что-то да значит)
а дополнительная ф-ция равна super.dispatchEvent в вашей реализации