Перевод статьи 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.
Плюс:
— более компактный код
Минус:
-производительность