Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Сообщения за день
 

Вернуться   Форум Flasher.ru > Flash > ActionScript 1.0/2.0

Версия для печати  Отправить по электронной почте    « Предыдущая тема | Следующая тема »  
Опции темы Опции просмотра
 
Создать новую тему Ответ
Старый 27.04.2007, 16:44
WindWalker вне форума Посмотреть профиль Отправить личное сообщение для WindWalker Найти все сообщения от WindWalker
  № 1  
Ответить с цитированием
WindWalker
[+1 18.03.08]

Регистрация: Nov 2006
Сообщений: 223
По умолчанию AS-алхимия: обработка события с доп. параметрами.

Очень многие новички, более-менее разобравшись с конструкцией типа
Код:
button.onRelease = onButtonRelease;
зачастую встают в ступор, когда требуется передать дополнительные параметры.

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

Большинство тех, кто делает первые шаги в изучении AS, первым делом пытается сделать что-то вроде такого:
Код:
btnMenu1.onRelease = this.onMenuReleased(1);
btnMenu2.onRelease = this.onMenuReleased(2);
btnMenu3.onRelease = this.onMenuReleased(3);
С точки зрения программирования - полнейшая чушь.
Но с другой стороны - интуитивно-понятная вещь (не зря же так много людей абсолютно независимо друг от друга совершает одну и ту же ошибку).

Класс mx.utils.Delegate не позволяет передавать дополнительные параметры. К счастью, его можно самостоятельно слегка модицифировать.
Тогда можно будет написать примерно так:
Код:
btnMenu1.onRelease = Delegate.create(this, this.onMenuReleased, 1);
btnMenu2.onRelease = Delegate.create(this, this.onMenuReleased, 2);
btnMenu3.onRelease = Delegate.create(this, this.onMenuReleased, 3);
Выглядит это крайне громоздко. Кроме того, приходится дублировать объект, обрабатывающий событие - в первом и во втором параметре.
И если this зачастую ещё можно и опустить, то если там нужно указать какой-нить конкретный объект (например, какой-нибудь menuHandler), то дублирование будет обязательным.

Я долгое время мечтал сделать способ задавать обработчки событий максимально близко к тому самому ошибочному, но интуитивно-понятному способу.

Теперь у меня код имеет такой стиль:
Код:
btnMenu1.onRelease = new Call(this).onMenuReleased(1);
btnMenu2.onRelease = new Call(this).onMenuReleased(2);
btnMenu3.onRelease = new Call(this).onMenuReleased(3);
Совсем чуть-чуть лишней мишуры и ошибочный подход стал вполне работоспособным.

Всё волшебство находится в классе Call.
Это настоящая алхимия и херомантия в одном флаконе.
Вот так выглядит класс:
Код:
dynamic class as.common.Call {    
    private var __oScope:Object;
    private var __fFunction:Function;
    private var __aArguments:Array;

    public function Call(oScope:Object) {
        __oScope = oScope;        
    }

    public function __resolve(sFunctionName) {  
        __fFunction = __oScope[sFunctionName];
        return __prepareCall;
    }

    private function __prepareCall() {
        var f = function() {            
            var scope = arguments.callee.oScope;
            var func = arguments.callee.fFunction;
            return func.apply(scope, arguments.concat(arguments.callee.aArguments));
        };
        f.oScope = __oScope;
        f.fFunction = __fFunction;
        f.aArguments = arguments;
        return f;
    }
}
Довольно короткий, но без бутылки не разберёшься

Итак, как он работает.
Разберём по порядку
new Call(this).onMenuReleased(1);

Выражение new Call(this) создает новый экземпляр класса Call, который сохраняет ссылку на объект-исполнитель в поле __oScope.

new Call(this).onMenuReleased(1);

Указанный затем метод (в данном случае - onMenuReleased) как бы относится уже к созданному экземпляру класса Call.
Но такого метода у этого класса нет, поэтому вызывается метод __resolve, который переопределён.
Этот метод находит по имени нужный метод в объекте-исполнителе (здесь делается предположение, что именно там и надо его искать), запоминает его в поле __fFunction, а возвращает ссылку на метод __prepareCall.

new Call(this).onMenuReleased(1);

Скобочки после названия метода означают, что его надо исполнить.
Однако исполняется у нас не onMenuReleased, а __prepareCall, ссылка на который была возвращена.

Этот метод делает последний штрих - берёт переданные параметры и создаёт специальную функцию (f), которая умеет уже запускать нужный нам метод от имени нужного объекта с нужными параметрами.
И именно эта специальная функция и присваивается в onRelease.

Таким образом, в реальности у нас происходят куда более хитрые процессы, чем это кажется на первый взгляд.
Но если воспринимать класс Call как "чёрный ящик", то использовать его крайне легко, и, думаю, многим новичкам он очень пригодится.

Старый 27.04.2007, 16:46
etc вне форума Посмотреть профиль Найти все сообщения от etc
  № 2  
Ответить с цитированием
etc
Et cetera
 
Аватар для etc

Регистрация: Sep 2002
Сообщений: 30,787
Дяденька, вы маленько опоздали. Все, кому надо было, уже лет пять назад такое сделали… Уж тем более __resolve тут нафиг не нужен, не говоря уже о том, что это не есть правильно.

Старый 27.04.2007, 16:48
screamge вне форума Посмотреть профиль Отправить личное сообщение для screamge Посетить домашнюю страницу screamge Найти все сообщения от screamge
  № 3  
Ответить с цитированием
screamge
Ветеран форума
 
Аватар для screamge

Регистрация: Jul 2006
Адрес: Грузия, Тбилиси
Сообщений: 2,675
Код:
class ru.inils.util.Delegate extends Object {
	/**
	 * Передача событий от любого объекта к любому объекту.
	 * 
	 * @usage			public static create (obj:Object, func:Function, arg:Array) : Function
	 * @param	obj		Object - Объект, целевая область видимости.
	 * @param	func	Function - Метод, обработчик данного объекта.
	 * @param	arg		Array [дополнительный параметр] - Массив передаваемых аргументов.
	 * @return  Function	- Функция обработчик.
	 */
	public static function create (obj:Object, func:Function, arg:Array):Function {
		var f = function () {
			var targetTemp = arguments.callee.target;
			var funcTemp = arguments.callee.func;
			var argTemp = arguments.callee.arg;
			return funcTemp.apply (targetTemp, argTemp);
		};
		f.target = obj;
		f.func = func;
		f.arg = arg;
		return f;
	}
}
Класс написанный iNils, передавайте параметры легко и просто.
__________________
Free-lance | Twitter | Me

Старый 27.04.2007, 16:49
WindWalker вне форума Посмотреть профиль Отправить личное сообщение для WindWalker Найти все сообщения от WindWalker
  № 4  
Ответить с цитированием
WindWalker
[+1 18.03.08]

Регистрация: Nov 2006
Сообщений: 223
__etc

Без __resolve будет выглядеть не так красиво.

screamge

О чём-то подобном я и говорил, когда писал про "немного модифицировать Delegate".
В вашем варианте вижу две проблемы:
1. Аргументы надо передавать массивом, что не очень красиво смотриться.
2. Некоторые события сами имеют параметры (например, killFocus), в вашем варианте они потеряются.


Последний раз редактировалось WindWalker; 27.04.2007 в 16:52.
Старый 27.04.2007, 16:53
etc вне форума Посмотреть профиль Найти все сообщения от etc
  № 5  
Ответить с цитированием
etc
Et cetera
 
Аватар для etc

Регистрация: Sep 2002
Сообщений: 30,787
Вам шашечки или ехать? Ради сомнительной красоты кода использовать dynamic и __resolve — перебор.
А что будет, если мы сделаем подобный делегейт в такой схеме:

Код:
addEventListener('anyEvent',{anyEvent:new Call(this).anyEventHandler(123)}
event-объект в первом аргументе мы, скорее всего, не получим. Сейчас проверим.


Последний раз редактировалось etc; 27.04.2007 в 16:56.
Старый 27.04.2007, 16:56
WindWalker вне форума Посмотреть профиль Отправить личное сообщение для WindWalker Найти все сообщения от WindWalker
  № 6  
Ответить с цитированием
WindWalker
[+1 18.03.08]

Регистрация: Nov 2006
Сообщений: 223
Согласен, компилятор перестанет находить ошибки.
Но в случае использования модифицированного Delegate тоже нет проверки, скажем, типов параметров.

А код действительно становится гораздо нагляднее и понятнее (если, конечно, не глядеть внутрь Call).

P.S.
А я знал, что __etc'у не понравится

P.P.S.
Перенесите в раздел для новичков

Цитата:
Сообщение от __etc
event-объект в первом аргументе мы, скорее всего, не получим. Сейчас проверим.
Получим-получим!
Не зря же у меня вот такое:
Код:
arguments.concat(arguments.callee.aArguments)


Последний раз редактировалось etc; 27.04.2007 в 17:03.
Старый 27.04.2007, 17:06
etc вне форума Посмотреть профиль Найти все сообщения от etc
  № 7  
Ответить с цитированием
etc
Et cetera
 
Аватар для etc

Регистрация: Sep 2002
Сообщений: 30,787
Да, получим, проверил уже.
Тем не менее, я уже говорил, что dynamic и __resolve в данном случае зло и применяется ради очень сомнительной выгоды. Я уже не говорю про то, что может быть непонятного в строках:

Код:
btnMenu1.onRelease = Delegate.create(this, this.onMenuReleased, 1);
?
Начинающих класс Call вообще сведет в полный тупик, нежели Delegate, потому как им придется выяснять, что это за __resolve, dynamic и т.п. А продолжающим вторая строка понятна абсолютно.


Последний раз редактировалось etc; 27.04.2007 в 17:11.
Старый 27.04.2007, 17:21
WindWalker вне форума Посмотреть профиль Отправить личное сообщение для WindWalker Найти все сообщения от WindWalker
  № 8  
Ответить с цитированием
WindWalker
[+1 18.03.08]

Регистрация: Nov 2006
Сообщений: 223
Да всё понятно.
Я сам подобной штукой долгое время пользовался (только называлось у меня Tools.delegate).

Но напрягало глаза и руки:
1. Дважды указывается объект-исполнитель.
2. Метод не похож на метод, а просто какой-то параметр. А когда таких делегейтов много, начинает пестрить.
3. Параметры идут как бы особняком, а не в скобочках после метода.

Вобщем, ещё раз повторяю: хотелось сделать максимально читабельный и интуитивно-понятный внешний вид.
Получилось реализовать с помощью жуткой алхимии, а чём сразу и заявил.
Никого не заставляю использовать, просто поделился идеей - что можно сделать и вот так.

Старый 27.04.2007, 17:24
iNils вне форума Посмотреть профиль Отправить личное сообщение для iNils Посетить домашнюю страницу iNils Найти все сообщения от iNils
  № 9  
Ответить с цитированием
iNils
Негуру
 
Аватар для iNils

администратор
Регистрация: Jan 2000
Адрес: Кёнигсберг in Moscow
Сообщений: 21,883
Записей в блоге: 7
Встану на защиту своих аргументов в модифицированом Delegate Можно конечно и не массивом их передавать, написать дополнительных пару строку не проблема. Но массив позволяет нам передавать аргументы, где-то заранее сформированные или динамические. Поэтому дело тут совсем не в красоте, а в практическом использовании.
__________________
(и)Нильс.ru | Плагины для FlashDevelop

Старый 27.04.2007, 17:25
screamge вне форума Посмотреть профиль Отправить личное сообщение для screamge Посетить домашнюю страницу screamge Найти все сообщения от screamge
  № 10  
Ответить с цитированием
screamge
Ветеран форума
 
Аватар для screamge

Регистрация: Jul 2006
Адрес: Грузия, Тбилиси
Сообщений: 2,675
максимально интуитивно понятным и тот и другой способ является для разбирающегося хорошо в кодинге, новичку сложнее будет разобраться с call так как он редко где встречается, уж поверьте мне
__________________
Free-lance | Twitter | Me

Создать новую тему Ответ Часовой пояс GMT +4, время: 04:38.
Быстрый переход
  « Предыдущая тема | Следующая тема »  

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


 


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


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