Sunday, July 18, 2010

Dependency injection and Gin

I was coming across the phrase "dependency injection" quite a bit while reading articles about GWT and MVP, and references to the Guice and Gin libraries coming out of Google. I figured it was some new programming paradigm and that I was hopelessly out of date not knowing what it was. The wikipedia entry didn't really help clear it up, nor did other articles I read. I kept getting the feeling that I was missing something, and that the concept wasn't really something new.

Turns out I was missing something, all right


I was missing that DI is giving a name to something that I and every other object oriented developer have been doing for years.

The short summary in this great article is what finally made me realize that I could stop trying to make it more complicated than it was: "Dependency injection means giving an object its instance variables. Really. That's it." If you're having trouble with the concept, James Shore's article is invaluable.

A conceptual example would be something like: you have an object that relies on a reference to some sort of data provider. In production, the provider will be a database back end, but during testing you want to mock up a provider that is always up and you can precisely control what errors it has.

You might solve this by constructing a factory that spits out the correct type of provider, and your object can ask that factory to give it a reference to the proper kind of provider. This works, but your testing has to construct, tear down, and reset the factory to the previous state between each test. You also have to write a lot of boilerplate code for each of these factories.

DI refers to giving the object a reference to the proper provider, which, during testing, allows you to build a new provider instance which you pass to the object during each test. As you exit the test, the object and provider are dereferenced automatically, saving you the cleanup. But in the code itself, you're now either passing provider references possibly through layer after layer of constructors, or you're building factories for the initial object which get the proper provider and pass it to the object, and then return the completed object.

Guice and Gin save you from all this repetitive factory building and wiring, and what provider each object needs can be declared in one place, or inline where the instantiated providers will be injected in the object.

I'm certainly not qualified at this time to discuss all of the nuances and capabilities of these libraries, since there are many different ways this concept could be used.

Instead, I'll talk about using Gin in this project


com/lisedex/volinfoman/Volinfoman.gwt.xml
<!-- Include Gin -->
<inherits name="com.google.gwt.inject.Inject" />
Adding those lines to your module's gwt.xml file, and adding the Gin and Guice jar files to your classpath, is all that it takes to enable the Gin annotations and compile time code generation.

If you go to the VolInfoMan project page on github, you can go to the basic-gin tag and download the source. The project is very, very basic at this point, and it's easy to find the one instance of using Gin.

As practice, I wanted to set up the home page you land on when loading the site to be capable of being changed by dependency injection. This will allow, later, changing the implementation of the page by making a new class that extends the skeleton Homepage class, and just changing the Gin binding in one place, and all references to that Homepage will be using my new implementation. Look ma, no factory!

com/lisedex/volinfoman/client/Homepage.java
package com.lisedex.volinfoman.client;

import com.google.gwt.user.client.ui.Composite;

public class Homepage extends Composite {
}

First we define our skeleton Homepage class, which will be added to our RootPanel later in onModuleLoad() (actually onModuleLoad2() due to our use of the gwt-log library).

com/lisedex/volinfoman/client/gin/VolinfomanGinjector.java
package com.lisedex.volinfoman.client.gin;

import com.google.gwt.inject.client.GinModules;
import com.google.gwt.inject.client.Ginjector;
import com.lisedex.volinfoman.client.Homepage;

@GinModules(VolinfomanModule.class)
public interface VolinfomanGinjector extends Ginjector {
 Homepage getHomepage();
}

We extend the com.google.gwt.inject.client.Ginjector class and use the @GinModules annotation to specify what class contains the binding configuration. We also define the getHomepage() injector method, which we need when using Gin in our application initialization code, because Gin is working at compile time to generate JavaScript, unlike Guice. Other dependencies that operate below our initialization code will be injected automatically, and won't require this type of method.

com/lisedex/volinfoman/client/gin/VolinfomanModule.java
package com.lisedex.volinfoman.client.gin;

import com.google.gwt.inject.client.AbstractGinModule;
import com.lisedex.volinfoman.client.DefaultHomepage;
import com.lisedex.volinfoman.client.Homepage;

public class VolinfomanModule extends AbstractGinModule {
        @Override
        protected void configure() {
                bind(Homepage.class).to(DefaultHomepage.class);
        }
}

We extend AbstractGinModule to declare our bindings for injection. Using AbstractGinModule instead of GinModule allows us to drop the binder.bind() syntax in favor of bind(). In the above code, we're declaring that when our code asks for a Homepage object, it will get a DefaultHomepage instead.

com.lisedex.volinfoman.client.DefaultHomepage actually contains no references to Gin. It extends Homepage but doesn't need to know anything about injection.

com/lisedex/volinfoman/client/Volinfoman.java
package com.lisedex.volinfoman.client;

import com.lisedex.volinfoman.client.gin.VolinfomanGinjector;
// import GWT objects here...

public class Volinfoman implements EntryPoint {
        public void onModuleLoad() {
                // we set up gwt-log here and
                // use a DeferredCommand to execute
                // onModuleLoad2() */
                // ...
        }

        private void onModuleLoad2() {
                // Create a ginjector
                VolinfomanGinjector ginjector = 
                    GWT.create(VolinfomanGinjector.class);
                // Add the homepage to the rootpanel
                RootPanel.get().add(ginjector.getHomepage());
        }
}

And our GWT entry point creates the Ginjector, and we use the injector method getHomepage() to inject an instance of DefaultHomepage, thanks to the binding we set up in VolinfomanModule. As mentioned above, we have to use an injector method due to current limitations in Gin.

That's it. Remember to grab the basic-gin tag of the source code from github for a compile-ready Eclipse project where you can immediately play with Gin.

Other Gin information


Gin supports some of the same annotations as Guice, such as @Inject, @ProvidedBy, @Singleton, @ImplementedBy, all of which allow you to move much of the binding information out to the source where the bindings occur. Much of this is to provide an alternative to using a GinModule to declare all of the bindings in one place; the latter is the option I prefer, but it's personal preference for the most part.

Some of the other bind() syntaxes are:
bind(Something.class).toProvider(SomethingProvider.class);
bind(Something.class).to(SomethingImplementation.class)
    .in(Elsewhere.class);
bind(Something.class).annotatedWith(SomethingAnnotation.class);
    .to(SomethingImplementation.class);
bind(Something.class).to(SomethingImplementation.class)
    .in(Singleton.class);

Line 1: When Gin injects an instance of a class normally, it uses the class's default constructor. If you need to give extra information to a constructor, you can use the toProvider() syntax. Gin will inject an instance of Something from the SomethingProvider wherever you need a Something class.

Line 2: If you only want a binding to apply in Elsewhere, instead of globally, you can use this syntax to specify that scope.

Line 4: You can also set up custom annotations. When you want multiple bindings for one type, you can specify which binding to use by setting up an annotation for each. The annotation and the type will uniquely identify which binding is appropriate. See the Guice wiki for more information.

Line 6: Gin's injector normally creates a new instance of each SomethingImplementation when it gets injected. However, sometimes you'll want to use the Singleton pattern, and only have one instance of SomethingImplementation running around. Remember that this is a Singleton per Ginjector, so if you have multiple Ginjectors (which, if you do, you probably need to double check your code) there will be one instance of the SomethingImplementation for each.

There's a bunch more that can be done with Gin; this is obviously only scratching the surface. Remember that Gin does the injection at compile-time, so there should be almost no overhead for using it, and it can save you a lot of repetitive coding. To understand all of the capabilities, it would behoove you to read the Guice User's Guide, the Gin tutorial, and the compatibility information between Gin and Guice. The documentation for Guice is much more thorough, but you have to know which ideas can be used in your GWT code. Guice will be used later for our server side code which runs under GAE.

No comments:

Post a Comment