Projekt: http://wiki.github.com/robertpenner/as3-signals/
A megoldás hasonlít a C#-ban ismert eseménykezelésre aminek lényege, hogy minden eseményhez tartozik egy objektum ami az Observer tervezési mintát implementálja. Egy Signal objektumot tekinthetünk úgy mintha egy EventDispatcher lenne így az osztályunk minden eseményhez egy saját dispatcher objektumot rendel amikhez eseményfigyelőket adhatunk.
// Általános button.addEventListener(MouseEvent.CLICK, onClick); button.dispatchEvent(new MouseEvent(MouseEvent.CLICK)) // Signal megfelelője button.clicked.add(onClicked); button.clicked.dispatch()
Az egyszerű esemény kezelésen kívül pár EventDispatcher-ben jelenleg nem megtalálható funkciót is támogat a Signals mint például:
- signal.addOnce() : Csak egyszer hívodik meg a hozzá adott figyelő meghívás után törlődik.
- signal.numListeners : A hozzáadott figyelők száma.
- signal.removeAll() : Az összes figyelő eltávolítása.
Most lássunk egy egyszerű osztályt ami hasznát veszi az új eseménykezelő rendszernek.
Worker.as
package
{
import org.osflash.signals.DeluxeSignal;
import org.osflash.signals.Signal;
import org.osflash.signals.events.GenericEvent;
public class Worker
{
public var name:String = "WorkerClass"
public var sigWorked:Signal
public var sigTargetWorked:DeluxeSignal
public function Worker()
{
sigWorked = new Signal(String, int)
sigTargetWorked = new DeluxeSignal(this)
}
public function work():void
{
sigWorked.dispatch("Working", 1)
sigWorked.dispatch("Working", 2)
sigWorked.dispatch("Working", 3)
}
public function workTargeted():void
{
sigTargetWorked.dispatch( new GenericEvent() )
}
}
}
Az osztályunk 2 Signal-t definiál az egyik egy sima alap Signal(sigWorked) aminek konstruktor paraméteren keresztül átadható hogy majd később mennyi és milyen típusú objektumot fog továbbadni. Ezeket futásidőben ellenőrzi így hibát kapunk ha nem megfelelően használjuk. Az elnevezésnél én szeretem elé írni a sig előtagot mivel így a kódkiegészítő listájában rögtön látni lehet hogy milyen események használhatók.
A work metódus meghívásával dobunk is 3 eseményt és paraméterként átadjuk a már megadott típusú objektumokat ezek a figyelő függvénynek átadódnak majd. Nem kötelező paramétert átadni ekkor csak simán meghívódik a figyelő függvény paraméterek nélkül.
A 2. Signal (sigTargetWorked) DeluxeSignal típusú ami annyival tud többet hogy egy speciális saját eseményt tud dobni aminek beállítja a target és currentTarget tulajdonságát hasonlóan mint az AS3 eseményei így az eseményt dobó osztály is elérhető. Ehhez a funkcióhoz a legalkalmasabb a GenericEvent amiből származtathatunk is saját osztályokat.
Most lássuk hogy tudjuk ezt az osztályt használni:
package
{
import flash.display.Sprite;
import org.osflash.signals.events.GenericEvent;
public class SignalsTest extends Sprite
{
private var worker:Worker
public function SignalsTest()
{
worker = new Worker()
worker.sigWorked.addOnce( onWorkOnce )
worker.sigWorked.add( onWorking )
worker.sigTargetWorked.add( onWorkTargeted )
trace("Listeners:",worker.sigWorked.numListeners)
worker.work()
worker.workTargeted()
worker.sigWorked.removeAll()
//Több esemény már nem íródik ki mert töröltük az összes figyelőt
worker.work()
}
private function onWorkOnce(msg:String, num:int):void
{
trace("Working once :", msg, num)
}
private function onWorking(msg:String, num:int):void
{
trace("Working listener :", msg, num)
}
private function onWorkTargeted(event:GenericEvent):void
{
trace("Targeted work from", Worker(event.target).name)
}
}
}
Output:
Listeners: 2 Working once : Working 1 Working listener : Working 1 Working listener : Working 2 Working listener : Working 3 Targeted work from WorkerClass
A kimeneten látjuk hogy először kiíratjuk a hozzáadott sigWorked figyelők számát majd az addOnce segítségével hozzáadott figyelő 1x lefut a többi pedig kiír mindent.
Az onWorkTargeted a GenericEvent segítségével kiíratja a signal forrását ami a Worker osztályunk volt.
A Signals jól együttműködik a beépített eseményekkel is ehhez készítsünk egy egyszerű szöveg betöltő osztályt ami majd a Signals segítségével valósítja meg az eseménykezelést. Ehhez a NativeSignal osztály lesz a segítségünkre ami a beépített eseménykezelő rendszer eseményeit alakítja saját eseményekké.
SignalsTextLoader.as
package
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import org.osflash.signals.Signal;
import org.osflash.signals.natives.NativeSignal;
public class SignalsTextLoader
{
private var urlLoader:URLLoader
public var sigCompleted:Signal
public var sigProgress:NativeSignal
public var sigIoError:NativeSignal
public var sigSecurityError:NativeSignal
public function SignalsTextLoader()
{
urlLoader = new URLLoader()
sigCompleted = new Signal(String)
sigProgress = new NativeSignal(urlLoader, ProgressEvent.PROGRESS, ProgressEvent)
sigIoError = new NativeSignal(urlLoader, IOErrorEvent.IO_ERROR, IOErrorEvent)
sigSecurityError = new NativeSignal(urlLoader, SecurityErrorEvent.SECURITY_ERROR, SecurityErrorEvent)
urlLoader.addEventListener(Event.COMPLETE, onLoadComplete)
}
public function load(urlReq:URLRequest):void
{
urlLoader.load(urlReq)
}
private function onLoadComplete(event:Event):void
{
sigCompleted.dispatch(urlLoader.data)
}
}
}
A NativeSignal első paramétere egy EventDispatcher amihez egy figyelőt regisztrál a 2. paraméterként megadott esemény típusával. A 3. paraméterben opcionálisan megadható az esemény osztály ami akkor lehet hasznos ha a dispatcher több azonos típusú eseményt is dob de más az esemény osztály.
Az osztály használatára egy példa:
package
{
import flash.display.Sprite;
import flash.events.ErrorEvent;
import flash.events.ProgressEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class SignalsLoaderTest extends Sprite
{
private var loader:SignalsTextLoader
public function SignalsLoaderTest()
{
loader = new SignalsTextLoader()
loader.sigCompleted.add(onLoaded)
loader.sigProgress.add(onProgress)
loader.sigIoError.add(onError)
loader.sigSecurityError.add(onError)
loader.load(new URLRequest("http://www.swf.hu/"))
}
private function onLoaded(text:String):void
{
trace("Text loaded:", text.substr(0, 50)+"...")
}
private function onProgress(event:ProgressEvent):void
{
trace("Progress", event.bytesLoaded)
}
private function onError(event:ErrorEvent):void
{
trace("Error when laoding:",event.text)
}
}
}
A Signals használatának egy nagy előnye hogy gyorsan lehet vele saját eseményeket definiálni és speciális objektumokat továbbadni nincs szükség mindig külön eseményt létrehozni és tesztek szerint sok esemény figyelő esetén gyorsabb is mint a beépített, mindez az egyszerűségéből adódik ezért pár olyan funkciót nem is támogat amit a jelenlegi eseménykezelő rendszer igen. Figyelő hozzáadásnál például nem lehet a speciális addEventListener-nél megszokott paramétereket megadni vagy jelenleg nem lehet a display listán vándorló eseményeket dobni. Ez utóbbin és a prioritás megadáson már dolgoznak úgyhogy azokat a következő verziókban már talán láthatjuk is.
Források:SignalsTest.zip




