WindWalker
27.04.2007, 16:44
Очень многие новички, более-менее разобравшись с конструкцией типа
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 как "чёрный ящик", то использовать его крайне легко, и, думаю, многим новичкам он очень пригодится.
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 как "чёрный ящик", то использовать его крайне легко, и, думаю, многим новичкам он очень пригодится.