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.
Nice post. I hate writing those boiler plate classes for things like delegates.
Does this approach make it any easier to write your stub implementations or do you still have to do that from scratch?
How do you switch between stub services and real services?
Hey Giles
You could argue that by default, it makes stubbing easier because it frees up time from the sameness of boiler plate implementations to focus on the different-ness of writing stubs.
However, I intend on using this project as a stepping stone to writing an implementation of Martin Fowler’s Self Initializing Fake, which should make the whole process of stubbing significantly easier.
I’ve started tinkering, and will throw up a working example once I get it going.