Dependency Injection And IoC Containers

05.10.2013

The dependency injection pattern is one of my all-time favorite patterns in software design. The concept, in its most simple form, is so simple yet so powerful.

In essence, it takes us from this:

var TweetStream = function() {
    this.twitter = new TwitterService();
};
 
TweetStream.prototype.streamTweets = function() {var tweets = this.twitter.getTweets();};
 
var stream = new TweetStream();
stream.streamTweets();

To this:

var TweetStream = function(twitter) {
    this.twitter = twitter;
};
 
TweetStream.prototype.streamTweets = function() {var tweets = this.twitter.getTweets();};
 
var twitter = new TwitterService();
var stream = new TweetStream(twitter);
stream.streamTweets();

So what’s the big deal?

In the first example, TweetStream creates the TwitterService instance. TweetStream is forced to have a knowledge of (1) the exact “class” it should use to communicate with Twitter, (2) where and how to access the constructor, and (3) how to create an instance and appropriately initialize the object (passing parameters to the constructor, calling methods after the fact, etc.).

In the second example, TweetStream does not need to know any of these things since TwitterService is created by a third party and later passed into TweetStream. TweetStream only needs to know how to interact with the instance it is passed.

Although seemingly benign, the additional knowledge that TweetStream is forced to have in the first example increases its coupling to its dependencies while decreasing its own cohesion. The additional coupling leads to code that can be difficult to test and less flexible at runtime. If we were to “inject” the dependencies rather than forcing TweetStream to create or find them, we can easily mock dependencies during unit tests and provide them directly to the subject being tested. At runtime, we can easily swap out the object being provided as a dependency based on rules or contexts.

Inversion Of Control Containers

So what does this have to do with an IoC container? An inversion of control container, or IoC container for short, assists in dependency injection and essentially fills three roles. These roles are often split between a container and an injector, but for simplicity I’ll address them as though they are one in the same. Let’s have a look at each role.

Dependency Rule Registry

In our case study, TweetStream knows it needs a service to talk to Twitter. Depending on the dependency injection implementation, TweetStream can request this dependency in a variety of ways. In JavaScript, where static typing, interfaces, and language-based annotations don’t exist, the dependency would generally be requested by a name in the form of a string. For example, TweetStream may state that it needs “twitterService” or “twitter” or “TheTwits”–whatever name seems reasonable. The important part is that some actor in the system needs to match up this name with a rule for how to create or retrieve the dependency.

That’s where the IoC container comes in. The IoC container might have rules like the following:

  • When “twitter” is requested, provide a new instance using the TwitterService constructor.
  • When “chat” is requested, provide whatever is returned from ChatFactory.
  • When “logger” is requested, provide a singleton instance of AsyncLogger.
  • When “dialog” is requested, provide this specific object.
  • When “rank” is requested, provide the value 1337.

Injector

Now with our rules defined, we can use them to create or retrieve a dependency. Let’s say our object to receive dependencies–which we’ll call our injectee–is provided to the IoC container for dependency injection. Let’s say, for example, the injectee requests “chat” and “dialog” dependencies. The IoC container looks up the rule mapped to “chat”, runs ChatFactory which happens to instantiate a chat object and configures it so it’s all ready to go, then injects the chat object into the injectee. The container then looks at the rule for “dialog”, and, by golly, when the rule was being configured on the IoC container, a specific object was provided to the IoC container that should always be used when the “dialog” dependency is requested. The IoC container then provides that specific object to the injectee. If another injectee later asked for the “dialog” dependency, the same object would be provided to that injectee as well. As you can see, the rules offer quite a bit of flexibility.

In summary, the injector uses rules to create or retrieve dependencies and inject them into objects requesting them.

Dependency Object And Value Registry

In our first two rules, we use a constructor and a factory to create dependency objects at the moment they are called for:

  • When “twitter” is requested, provide a new instance using the TwitterService constructor.
  • When “chat” is requested, provide whatever is returned from ChatFactory.

On the other hand, some rules require that the IoC container hold onto dependency objects or values so they can be provided as dependencies to injectees at a later time rather than being re-created each time they are requested. The last three rules I listed pertain here:

  • When “logger” is requested, provide a singleton instance of AsyncLogger
  • When “dialog” is requested, provide this specific object.
  • When “rank” is requested, provide the value 1337.

In each of these, the container is holding onto objects or values so they can be provided as dependencies to injectees at a later time. In this manner, the IoC container becomes a registry of objects and values.

I’m kind of sneaking this in here, but in the case of the rule providing a singleton instance, you can avoid the woes of self-enforced singletons and reap the bounties of context-based singletons. Oh the wonders of IoC containers! Check out this post by Joel Hooks for more on this.

Less Hand-waving, More Code

To make a long story short, I’ve been playing around with dependency injection in JavaScript lately. It’s still fairly new territory in this JavaScript world of ours though is becoming increasingly more popular thanks in large part to AngularJS which provides dependency injection out of the box. DeftJS is also melding dependency injection into frameworks like Ext JS and Sencha Touch.

In my frolicking about a year ago, I felt like cooking up an expressive, dependency-free, lightweight IoC container I called Injector.js which you can find on GitHub. Although the code may happen to prove useful for someone, I mention it here with the mere hope that the examples in the readme and the unit tests can help solidify some of the concepts I’ve discussed here.

In my experience, IoC containers become most powerful when integrated with an application framework that can auto-inject dependencies while requiring as little boilerplate as possible from the developer. Although this isn’t intended to be a sales-pitch, AngularJS has nicely integrated dependency injection and I recommend becoming familiar with its workflow even if just for the learning opportunity it provides.

More Reading And Watching

Check out this fantastic presentation by Miško Hevery on why dependency injection is so important for clean and testable code. Seriously, don’t miss it.

If you’re the reading type, check out this article by Martin Fowler for a more formal and authoritative description of inversion of control and dependency injection.

Tags: , , ,


Comment

07.22.2013 / Romuald said:

Hi there,

Nice article! I love DI too.

You might like a standalone DI library I built for javascript (infuse.js):
https://github.com/soundstep/infuse.js

The API is very simple, powerful and inspired by a very neat one called swiftsuspenders.

I’ve included the DI library in the soma.js framework, here is an article that demonstrate the power of injection in terms of decoupling:

http://flippinawesome.org/2013/07/15/soma-js-your-way-out-of-chaotic-javascript/

Hope you like all this.

Romu


Leave a Comment

Your email address is required but will not be published.




Comment