Pages

Tuesday, March 20, 2012

DI with Guice for Java Swing

If you're familiar with Dependency Injection (DI) concepts and frameworks such as Spring,
you know the great benefits it introduces when developing an application.
Spring which I daily use (and praise), is a framework (or an application platform if to be more accurate) that one of its core features and philosophy is DI.

Some of the benefits of DI are well known:

  1. Simplified code.
  2. Reduced dependencies.
  3. Helps 'modularity' and creation of reusable components.
  4. Helps you test your application better.
  5. Lazy loading.


Surely there are other benefits you could think of which were not mentioned above.
So, why not bring all that good stuff to your Rich Client ?

The connection of DI to the title of this post is how to integrate DI in your front-end in case you are building a Rich client application, using Java Swing or GWT for example.

You could follow the MVC pattern (which is well known, and can be great) to build your rich client, and integrate it with a DI framework. However I strongly recommend you consider the MVP pattern for an even better design of your front-end. Which I find it to be much 'cleaner'.
(In MVC some of the business-logic code is scattered between the controller and the View. It's harder
to re-use your View objects/Widgets, especially in case you want to give it to someone else to use, or integrate them in another product).

Smarter people than me talk about it, and you can find great talks on YouTube regarding this subject.

You can later also check Google I/O's excellent talk for "Best Practices for Architecting GWT App"  http://www.youtube.com/watch?v=PDuhR18-EdM

So, if we follow the MVP design pattern and Google's best practices (even if we don't use GWT),
we end up with few key components in our app, i'll try to give some description what they do:

  1. "The" Event Bus - The component where presenters 'listen' to, and can fire events to.
  2. The Presenters - The components which encapsulate the logic of your application.
  3. The Views / Displays - Simply a view - can be a panel with widgets in it. No logic in here!
  4. A Model - Where you keep important data that can be accessed by one or more presenters.


"Show me the code!"

Lets assume you're writing your rich client in Java Swing.
We will use the lightweight DI framework "Guice" by Google:
http://code.google.com/p/google-guice/ to help us achieve our goal.

Lets go over some steps to realize what needs to be done in order to get this to work - don't forget to download Guice and put it's Jars in the classpath.
(Or, scroll all the way down to download the zip file containing the source code).


1) Define a simple View as a dumb object, not knowing anything about logic:
public class ContentPaneDisplay extends JPanel { .... }

2) Define a high level IPresenter interface and the IContentPanePresenter interface:

public interface IPresenter {
/**
* A method of all implementing presenters for presenting themselves on a given container.
* @param c
*/
void go(Container c) ;
}

public interface IContentPanePresenter extends IPresenter {}


3) Define a Presenter that will use view object above as its display:

public class ContentPanePresenter implements IContentPanePresenter {
public static interface IDisplay {
JComponent getAsComponent() ;
}
private final IDisplay display ;
....
}

4) Make ContentPaneDisplay implement the ContentPanePresenter.IDisplay interface.

5) Configure the Display class and the Presenter class to be used by Guice:

public class ApplicationDIModule extends AbstractModule {
@Override
protected void configure() {
// Lets configure some stuff !
bind(IContentPanePresenter.class).to(ContentPanePresenter.class).in(Singleton.class) ;
bind(ContentPanePresenter.IDisplay.class).to(ContentPaneDisplay.class).in(Singleton.class) ;
}
}

6) Add the @Inject annotation to the ContentPanePresenter to be injected the display:

/**
* C'tor
* This is where DI takes place!!!
* Guice will instantiate and provide this presenter with a matching display!
* @param display
*/
@Inject
public ContentPanePresenter(IDisplay display) {
this.display = display ;
...
}


7) Now all that's left is to call the Guice injector to get the ContentPanePresenter:
private static void go(Container container) {
// Guice.createInjector() takes your Modules, and returns a new Injector instance.
   Injector injector = Guice.createInjector(new ApplicationDIModule());
// lets get the 'Main' content pane Presenter !!
   IContentPanePresenter contentPanePresenter =
                                                            injector.getInstance(IContentPanePresenter.class) ;
contentPanePresenter.go(container) ;
}