I just pushed a new Parsley extension project to GitHub – support for the <DynamicService /> tag.
Typically in a project, I like to structure my services layers pretty cleanly – for each service I would declare the following:
- An interface which defines the services contract
- A concrete implementation of the interface, which abstracts away the RemoteObject
- A stub implementation of the interface, for testing when the services layer is unavailable
For example, a simple EchoService might look a little something like this:
// IEchoDelegate.as
public interface IEchoDelegate
{
function echoMessage(source:String):AsyncToken;
}
// EchoDelegate.as
public class EchoDelegate
{
[Inject]
public function service:RemoteObject;
public function echoMessage(source:String):AsyncToken
{
return service.echoMessage(source);
}
}
Here, you can see the concrete delegate is really just boiler-plate code. It’s pretty repetitive, and while I find lots of value in the pattern, the actual implementation can be a bore.
So, I put together a Parsley extension to generate these delegates on the fly.
Here’s an example:
<?xml version="1.0" encoding="utf-8"?>
<parsley:Objects>
<fx:Declarations>
<services:DynamicService type="{IEchoService}" endpoint="http://localhost:8080/testdrive/messagebroker/amf" destination="echoService" />
</fx:Declarations>
</parsley:Objects>
This definition combines the Interface and the remote object defininition in the context. An implementation of the interface is generated on-the-fly at runtime, and is available to be injected into classes as required.
Eg:
// EchoCommand.as
public class EchoCommand
{
[Inject]
public var service:IEchoService;
public function execute(message:EchoMessage):AsyncToken
{
return service.echo(message.source);
}
public function result(result:String):void
{
trace("Received from the server:" + result);
}
}
Source
The source for this is available now on github. There’s also a demo project available here
A word of warning
Under the covers, this extension makes use of the ASCommons-Bytecode library to build an implementation of the interface.
Unfortunately, this dynamic goodness is not free, and incurs a one-off cost at startup when the ByteCode library parses the bytecode of the swf into memory. As a result, you’ll notice that Parsley takes a little longer to intitialize than you might be used to. This is a tradeoff that must be considered before using this approach.