web stats

Dependency Injection in Closure – IoC with Loader

Github here: Loader

Closure Library deals with dependencies at compile time creating a graph from those goog.require and goog.provide statments at the top of your files. Unfortunately this means that our dependencies are hardcoded to each file and we can get in trouble if there are circular dependencies.

The way to relieve this is to pass in the dependencies to our classes when they are instantiated. This way we can keep the majority of our dependencies in a single file and pass through the implementations that classes need. This means we have code that used to look like this:

goog.require('myImplementation');

myClass = function() {
this.myImplementation = new myImplementation();
}

can now become this:

myClass = function(myImplementatation)() {
this.myImplementation = new myImplementation();
};

We’ve moved the requires from being explicitly defined at the start to being something we have to define when we instantiate the class. If we do this throughout our program we can pass along dependencies all the way from the beginning. This not only means that we don’t have to worry about our dependency graph but it has the added bonus that we can pass in mocks and stubs as the dependencies and make it easy to test each individual class without having to rely on our explicitly defined dependencies.

The big issue then is that this may get tricky the larger your program becomes. If you have to start passing along dependencies through parent files just to give them to the children then this can get messy quickly. To make this easier we can use a resource locater that has the ability to find the implementation we need. This is what Loader does for you.

Loader is based off the AngularJS dependency system but is made to work with Advanced Optimizations so differs in how it’s used but also has a couple of other features such as instantiation of objects when their dependencies are met, in that way it also has some similarity to AMD

Loader is designed to be required in your main file where you will declare all the dependancies:

require('Ldr');

Once you’ve done that then Ldr is available for your entire program. The next thing you need to do is write your classes in the method described above, putting your dependencies at the start of your constructor function. If your constructor function also takes other parameters then you can place them afterwards, but your classes really shouldn’t have any extra parameters (if they do then you can use them as dependencies only when they are singletons, though they can be instantiated without any problems).

Next you’ll need to add a line to the prototype of your class to help Loader get the dependencies for you. It should be an array of strings that are the names for your dependencies:

myClass.prototype.inject_ = ['myImplementation'];

Now your class is Loader friendly with the added bonus that you can still use this class as is even if you copy it to a project where you’re not using Loader.

Next thing we need to do is go to the main file and require in all our dependencies. Once they are there we can tell Loader to store the dependencies for when an invocation of a class is needed.

There are three different types of dependencies in Loader:

1. Constants
These are for when you want the dependency passed in as is. For instance you may need to pass in a constructor function, Loader will try and instantiate any dependencies given so if you want the constructor instead of an instance then you’ll want to pass it in like this:

Ldr.regConst('name', myConst);
2. Regular

These are for classes that you want to be instantiated before being passed to the class. Though it is made for Classes it can also take objects and will pass them directly through. These are registered like this:

Ldr.reg('name', depClass);

3. Singletons

You can use this if you the object is meant to be a singleton. Although you can also just instantiate the object and pass it in to the regular method this has the added bonus that the singleton will only be instantiated when it’s own dependancies are satisfied and will return a promise that you can use to do extra setup like so:

Ldr.regSingle('mediator', app.Mediator, arg1).next(function(mediator) {
mediator.on('thing', function() {do stuff();});
});

Now that we’ve registered the dependencies we can go ahead and instantiate our class that has it’s own dependencies. It will pass the dependencies in recursively so we only have to call our top class. We can also pass in any arguments it might take like so:

var myInst = Ldr.inst(myClass, arg1);

There may be issues if a dependency is not registered and requested. We can test that all dependencies can be satisfied before instantiating by testing our class like so:

if (Ldr.test(myClass))
var myInst = Ldr.inst(myClass, arg1);

Or we can instead use the instSoon method which will return a promise that will run functions when the  dependencies are met. This is a great way to get around the circular dependency issue that might come up with the usual method of static dependencies in the Closure Library:

Ldr.instSoon(myClass, arg1)
.next(function(myInst) {myInst.foo();})
.next(function(myInst) {myInst.bar();});

There is just one trick left though. Dependencies like this work great for methods you have written, but what happens if you are using third party libraries that can take in constructors that they instantiate using the “new” keyword instead of Ldr.inst? Well we can make it so calling “new” on a function will instead run it through the instantiation steps of Ldr. All we need to do is this:

var toPass = Ldr.makeInst(myClass);

Now we can pass that in to other libraries with methods that take constructors such as PlastronJS and all the dependencies will be resolved when “new” is called on it.

Hopefully that will help you get started managing your dependencies, Loader is still quite new so there may be some changes but I’m sure they will all be for the better.

Powered by WPeMatico