Route Framework: Sensors and Routing
In Model View Controller or Delegate Model architectures special place is reserved to messaging system. When it comes to ActionScript, we usually utilise one of three solutions:
flash.events.Event/flash.events.IEventDispatcher.
custom Observer implementation.
AS3 Signals by Robert Penner.
Usually Application Framework comes tightly coupled with some or another messaging library. It might work in many cases, but sometimes it might be a serious bottleneck*. Route Framework can switch messaging systems easily by subclassing just 2 classes: Sensor, that will receive messages, and Route to wrap sensor and pattern in it.
Let’s consider following example with custom Observer Implementation:
IMessage.as
package observer
{
public interface IMessage
{
/**
* IMessage name, can be used for additional filtering.
*/
function get name():String;
/**
* @private
*/
function set name( value:String ):void;
/**
* Any data that can be sent with IMessage
*/
function get data():Object;
/**
* @private
*/
function set data( value:Object ):void;
}
}
IObserver.as
package observer
{
public interface IObserver
{
/**
* Invoked automatically by instance of IObservable. Should contain any update logic.
*
* @param message IMessage sent by IObservable
*/
function update( message:IMessage ):void;
}
}
IObservable.as
package observer
{
public interface IObservable
{
/**
* Adds an instance of IObserver to notify queue.
*
* @param observer IObserver to be added.
*/
function add( observer:IObserver ):void;
/**
* Use this methods, to notify all instances of IObserver added to current IObservable.
*
* @param message IMessage to be sent.
*/
function notify( message:IMessage ):void;
}
}
You can check actual implementation of this interfaces in sources, but implementation does not really matter in this case.
In order to teach Route to accept Messages all we need is to subclass Sensor and Route as follows:
package observer
{
import eu.kiichigo.route.pattern.*;
import eu.kiichigo.route.pattern.match.type;
import eu.kiichigo.route.routes.*;
/**
* Version of IRoute to work with custom Observer pattern implementation.
* Fluent style:
*
* route( Receive, { from: "idOfObserver" } );
*
* MXML Syntax:
*
*
*
*
* @author David "nirth" Sergey
*
*/
public class Receive extends Route implements IRoute
{
public function Receive()
{
super();
// Setup pattern to filter out messages of correct type.
var p:IPattern = new Pattern;
p.matcher = type;
p.store( "type", IMessage );
// Inject pattern.
pattern = p;
// Inject Sensor.
sensor = new ObserverSensor;
}
// Receive.from is simple proxy get-set accesor
/**
* @copy ObserverSensor#from
*/
public function get from():Object
{
return ( sensor as ObserverSensor ).from;
}
/**
* @private
*/
public function set from( value:Object ):void
{
( sensor as ObserverSensor ).from = value;
}
}
}
import eu.kiichigo.route.sensors.Sensor;
import observer.IMessage;
import observer.IObserver;
import observer.Messenger;
class ObserverSensor extends Sensor implements IObserver
{
/**
* @copy observer.IObserver#update
*/
public function update( message:IMessage ):void
{
send( message );
}
/**
* @private
*/
protected var _from:Object;
/**
* Indicates an instance of IObservable that this ISensor is listening too.
*/
public function get from():Object
{
return _from;
}
/**
* @private
*/
public function set from( value:Object ):void
{
//ToDo: Handle removal from old Messenger here.
_from = value;
Messenger.get( value ).add( this );
}
}
Now our class has nice readable class name and accessor name, and it’s actually reads as: “Receive from accessorId” as in example:
const router:IRouter = new Router().
route( Receive, { from: "leftButtons" } ).
action( Run, { closure: Alert.show, arguments: ["Received message from leftButtons"] } ).
route( Receive, { from: "rightButtons" } ).
action( Run, { closure: Alert.show, arguments: ["Received message from rightButtons"] } ).
end;
Alternatively you can use MXML Syntax too:
<route:Router>
<observer:Receive from="leftButtons">
<route:Run closure="{ Alert.show }" arguments="{ ['Received message from leftButtons'] }" />
</observer:Receive>
<observer:Receive from="rightButtons">
<route:Run closure="{ Alert.show }" arguments="{ ['Received message from rightButtons'] }" />
</observer:Receive>
</route:Router>
* Another framework that handles decoupling of messaging from mvc logic very nicely is Parsley framework.