Twitter can be a funny beast, what makes it great can also make it poor. I use Twhirl which keeps me updated any time one of the people I follow tweets about something, the only problem is that so many people tweet that if I dont happen to see it within about and hour or so of the Tweet, ill miss it. This time however I was lucky enough to catch a tweet by @Joa about his new Inversion of Control and functional-programming-like library, Funk AS3.
As I have been getting well into RobotLegs (a Dependency Injection MVCS framework) recently I was extremely interested to hear about this new project by Joa who I respect very much as a brilliant coder not least because of his excellent work on low-level Flash byte-code optimisation (see Apparat).
Joa has taken a different approach to doing dependency injection. The approach most frequently used (and the one used in SwiftSuspenders / RobotLegs) is to use meta-data to declare to a number of variables for injection. You then map a class to be injected and instantiate it using the injector.
As an example, with Swift Suspenders you would define a class for injection with something like the following:
[codesyntax lang="actionscript3" lines="normal" tab_width="4"]
class MyInjectedClass
{
public function sayHello(toSay:String)
{
trace("Hello "+toStay);
}
}
var injector : Injector = new Injector();
injector.mapSingleton(MyInjectedClass);
[/codesyntax]
Here we are telling the Injector to make a single instance of our class and hold it internally ready for when it is next requested, such as:
[codesyntax lang="actionscript3" lines="normal" tab_width="4"]
class MyDependantClass
{
[Inject] public var myClass : MyInjectedClass;
public function performAction()
{
myClass.sayHello("World");
}
}
[/codesyntax]
Here the [Inject] meta-data indicates that we want the framework to supply the class with an instance of type MyInjectedClass. The final part is to make an instance of the dendant class and inject into it:
[codesyntax lang="actionscript3" lines="normal" tab_width="4"]
var dependant : MyDependantClass = new MyDependantClass();
injector.injectInto(dependant);
dependant.performAction();
[/codesyntax]
As you can see this is a nice way of handling inter-module dependencies in your code, when coupled with a MVC framework such as RobotLegs it becomes and extremely powerful yet elegant way of coding.
It however isnt perfect and Joa, on his google code page mentions three drawbacks of this method:
- Your injected properties are publicly exposed and mutable.
- describeType is very expensive.
- Steep learning curve.
This is where he suggests his alternative method, which is quite ingenious. Using the same example as above you would see something like the following:
[codesyntax lang="actionscript3" lines="normal" tab_width="4"]
class MyInjectedClass
{
public function sayHello(toSay:String)
{
trace("Hello "+toStay);
}
}
bind(MyInjectedClass).asSingleton();
[/codesyntax]
Then the dependant class would look like the following:
[codesyntax lang="actionscript3" lines="normal" tab_width="4"]
class MyInjectedClass
{
protected var myClass : MyInjectedClass = inject(MyInjectedClass);
public function performAction()
{
myClass.sayHello("World");
}
}
[/codesyntax]
And making an instance of it could be as simple as:
[codesyntax lang="actionscript3" lines="normal" tab_width="4"]
var dependant : MyDependantClass = new MyDependantClass();
dependant.performAction();
[/codesyntax]
As can be seen there are some benefits to this method, the biggest one in my opinion is that injected properties dont have to be public as they are provided by the call from within the class scope rather than from outside.
So how does Joa perform this magic? By abusing a little used ability of the Actionscript programming language known as package-level-functions. These are throwbacks from the old AS1 & AS2 days of global functions. There are actually a couple of common examples in AS3 still such as getTimer() and getQualifiedClassName() still used. What Joa has done is to use these package level functions as a method of generating concise looking code reminiscent of functional programming.
Performance wise, im not entirely sure whether by using package-level functions instead of describeType() calls used in meta-data driven IoC frameworks is any faster as Till Schneidereit of Swift Suspenders suggests:
I don't think that Funk's approach is any faster than an optimized
metadata-based IoC container: describeType may be slow (as in "takes a
few dozen microseconds to run"), but is only ever called once for each
class an instance of which is injected into. After that, it's just a
straight iteration over an array for all injection points instead of
Funk's multiple method calls for each injection point.
So the next step for me is to run some tests to see how things pan out. Either way im very impressed with both approaches and cant wait to see what kind of exciting advances will be developed in the coming months.
It's worth pointing out that while Joa's approach is indeed IOC it's not Dependency Injection: each property is explicitly "fetching" what it needs as apposed to being "supplied" what it needs. This pushes it towards the Service Locator pattern and away from DI. It also couples classes that use it to itself (which DI doesn't do). That said, it's a novel approach, and fits really nicely with his Functional library - it's a nice way to do IOC when dealing with immutable structures, for example.
ReplyDeleteThanks for sharing Michael!
ReplyDeleteGood post, looking at robotlegs at the moment. But you mentioned 3 drawbacks to Robot Legs, one of which was: Steep learning curve.
ReplyDeleteSteep learning curve is a good thing. Knowledge gained over time. Time is always on the X axis, knowlege gained on the Y axis.
"The phrase "steep learning curve" is sometimes used incorrectly to mean "hard to learn" whereas of course it implies rapid learning.": http://encyclopedia2.thefreedictionary.com/learni...
Cheers Brian, im glad you liked the post. Yes you are correct, I intended it to mean that it can be tricky to pick up if you are not used to design patterns or thinking in the MVC modular-like way.
ReplyDeleteP.S. I still use RL alot and really enjoy it. Think I may write some more posts on the subject soon!
This isn't Inversion of Control. The "Inversion" part means that the child component, which under old architectures would be responsible for going and retrieving the things it needs to satisfy its dependencies, gives up that control (to a knowledgeable parent or a special container whose only responsibility is to satisfy dependencies. This is Inversion of Inversion of Control, or basically the old way with a shiny new face on it. The ModelLocator/ServiceLocator/Random Singleton has been replaced by package level methods.
ReplyDeletePost is over a year old now, but I hear what you are saying. Package level functions are never a good idea, they are also slow performance wise, I just thought it was a pretty clever method of implementing DI and created some interesting AS3 syntax.
ReplyDeleteYeah, I ran across it looking for information on performance of the Injector in RobotLegs. I'd say again that this doesn't really count as Dependency Injection, as the dependency is _not_ being injected. Instead, the component is getting the dependency for itself. The method of getting it may be somewhat abstracted, but it's some other thing than Dependency Injection.
ReplyDeleteYep, I spose you are right in that. In SwiftSuspenders (RL) you are meerly declaring that you have a dependency via the metadata [Inject] rather than acually going out and getting that dependency yourself its being supplied to you from outside.
ReplyDelete