Перевод статьи Aiden Tailor о сигналах, которые мигрировали на ActionScript из C# благодаря усилиям Rober Penner. Это не просто перевод, а шпаргалка — выжимка статьи. Без воды, ничего лишнего, чтобы даже самый ленивый мог пробежать глазами. Также произведены тесты производительности. Пост не только для AS3 гуру, но и и для всех, кого волнуют вопросы повышения качества кода, так как содержит описание одной из реализаций шаблона проектирования Observer. А вот видеоурок.
По своей сути Сигнал — это отдельной менеджер событий со своим массивом подписчиков. Основная идея — уйти от строковых сравнений, используемых в EventDispatcher и порождения набора Event-ов. Исходники as3 библиотеки можно забрать из GIT-репозитория тут.
Signal — основной сигнал.
Передача параметров происходит перечислением типов в конструкторе сигнала:
Garbage Collector съедает инстансы, если отписаться (проверил лично):
DeluxeSignal — роскошный (расширенный) сигнал позволяет получить доступ к владельцу сигнала и к самому сигналу.
Он может передавать в листенер как GenericEvent (общее событие), так и кастомные параметры (как базовый сигнал). Использовать его — иногда непозволительная роскошь (см. тесты производительности в конце поста)
NativeSignal — нативный сигнал. Служит для конвертирования родных ActionScript событий в сигналы.
Пример:
Производительность:
— профайл показал, что Signal в среднем отжирает дополнительно 4 кб оперативки на инстанс по сравнению в EventDispatcher
— Signal не более, чем в 2 раза медленнее, чем EventDispatcher
— Deluxe Signal примерно в 4,5 раза медленнее, чем EventDispatcher (теперь ясно, почему такое называние)
— NativeSignal — не тестировался, но анализ кода показал, что производительность будет примерно такая-же, как и у Signal
Код на котором производился тест скорости выполнения:
Выводы:
По сути Сигналы являются тем-же патерном Наблюдатель (Observer), то есть альтернативой IEventDispatcher. Производительность ниже, так как библиотека не нативная, но на приемлемом уровне. Использование сигналов может открыть миграцию других, завязанных на них решений, из C# на ActionScript.
Плюс:
— более компактный код
Минус:
-производительность
По своей сути Сигнал — это отдельной менеджер событий со своим массивом подписчиков. Основная идея — уйти от строковых сравнений, используемых в EventDispatcher и порождения набора Event-ов. Исходники as3 библиотеки можно забрать из GIT-репозитория тут.
Signal — основной сигнал.
package insideria.basicSignal {
import org.osflash.signals.Signal;
public class AlarmClock {
public var alarm:Signal;
public function AlarmClock() {
alarm = new Signal(); // создаем инстанс
}
public function ring():void {
alarm.dispatch(); // вещаем сигнал (событие)
}
}
}
package insideria.basicSignal {
import flash.display.Sprite;
public class WakeUp extends Sprite {
private var alarmClock:AlarmClock;
public function WakeUp() {
alarmClock = new AlarmClock();
alarmClock.alarm.add(onRing); // вешаем листенер
alarmClock.ring();
}
private function onRing():void { // обработчик
trace("Wake up!");
}
}
}
Передача параметров происходит перечислением типов в конструкторе сигнала:
package insideria.basicSignalArguments {
import org.osflash.signals.Signal;
public class AlarmClock {
public var alarm:Signal;
public function AlarmClock() {
alarm = new Signal(String); // через запятую типы параметров
}
public function ring():void {
alarm.dispatch("9 AM"); // бродкаст
}
}
}
package insideria.basicSignalArguments {
import flash.display.Sprite;
public class WakeUp extends Sprite {
private var alarmClock:AlarmClock;
public function WakeUp() {
alarmClock = new AlarmClock();
alarmClock.alarm.add(onRing);
alarmClock.ring();
}
private function onRing(time:String):void { //параметры должны быть того-же типа, что и в конструкторе сигнала
trace("Wake up! It's already " + time);
}
}
}
Garbage Collector съедает инстансы, если отписаться (проверил лично):
alarmClock.alarm.remove(onRing); // отписаться
alarmClock.alarm.addOnce(onRing); // отписывается после срабатывания, полезно, например, для Event.ADDED_TO_STAGE
alarmClock.alarm.removeAll(); // отписать все слушатели
DeluxeSignal — роскошный (расширенный) сигнал позволяет получить доступ к владельцу сигнала и к самому сигналу.
Он может передавать в листенер как GenericEvent (общее событие), так и кастомные параметры (как базовый сигнал). Использовать его — иногда непозволительная роскошь (см. тесты производительности в конце поста)
package insideria.deluxeSignal {
import org.osflash.signals.DeluxeSignal;
import org.osflash.signals.events.GenericEvent;
public class AlarmClock {
public var alarm:DeluxeSignal;
public var message:String;
public function AlarmClock() {
alarm = new DeluxeSignal(this); //передается ссылка на target объект
message = "This is a message from our AlarmClock";
}
public function ring():void {
alarm.dispatch(new GenericEvent()); // инстанциируется GenericEvent
}
}
}
package insideria.deluxeSignal {
import org.osflash.signals.events.GenericEvent;
import flash.display.Sprite;
public class WakeUp extends Sprite {
private var alarmClock:AlarmClock;
public function WakeUp() {
alarmClock = new AlarmClock();
alarmClock.alarm.add(onRing);
alarmClock.ring();
}
private function onRing(event:GenericEvent):void {
trace(event.target); // target, который передавался в конструкторе сигнала
trace(event.signal); // сигнал
trace(event.target.message);
}
}
}
NativeSignal — нативный сигнал. Служит для конвертирования родных ActionScript событий в сигналы.
Пример:
package insideria.nativeSignals {
import org.osflash.signals.natives.NativeSignal;
import flash.display.Sprite;
import flash.events.Event;
public class AddToStage extends Sprite {
public function AddToStage() {
var added:NativeSignal = new NativeSignal(this, Event.ADDED_TO_STAGE, Event); // способ полностью уйти от использования Event'ов
added.addOnce(onAdded);
}
private function onAdded(event:Event):void {
graphics.beginFill(0xCCCCCC);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}
}
}
Производительность:
— профайл показал, что Signal в среднем отжирает дополнительно 4 кб оперативки на инстанс по сравнению в EventDispatcher
— Signal не более, чем в 2 раза медленнее, чем EventDispatcher
— Deluxe Signal примерно в 4,5 раза медленнее, чем EventDispatcher (теперь ясно, почему такое называние)
— NativeSignal — не тестировался, но анализ кода показал, что производительность будет примерно такая-же, как и у Signal
Код на котором производился тест скорости выполнения:
public class Main extends Sprite {
private static const COUNT:int = 10000;
private var _signal: SignalItem;
private var _dispatcher: DispatcherItem;
private var t:int;
private var i:int;
private var test:int;
public function Main() {
_signal = new SignalItem();
_signal.signal.add(onSignal);
test = 0;
t = getTimer();
for (i = 0; i<COUNT; i++) {
_signal.dispatch();
}
t = getTimer()-t;
trace("Main.Main(); signal : " + t);
_dispatcher = new DispatcherItem();
_dispatcher.addEventListener(Event.CHANGE, onChange);
test = 0;
t = getTimer();
for (i = 0; i<COUNT; i++) {
_dispatcher.dispatch();
}
t = getTimer() - t;
trace("Main.Main(); dispatcher : " + t);
}
private function onSignal(z:int): void {
test++;
}
private function onChange(e:Event): void {
test++;
}
}
public class SignalItem {
private var _signal:DeluxeSignal;
public function SignalItem() {
_signal = new DeluxeSignal(this);
}
public function get signal(): Signal {
return _signal;
}
public function dispatch():void {
_signal.dispatch(new GenericEvent());
}
}
public class DispatcherItem extends EventDispatcher{
public function DispatcherItem() {
}
public function dispatch ():void {
dispatchEvent(new Event(Event.CHANGE));
}
}
Выводы:
По сути Сигналы являются тем-же патерном Наблюдатель (Observer), то есть альтернативой IEventDispatcher. Производительность ниже, так как библиотека не нативная, но на приемлемом уровне. Использование сигналов может открыть миграцию других, завязанных на них решений, из C# на ActionScript.
Плюс:
— более компактный код
Минус:
-производительность