Google Guice - Example

Posted on February 21, 2010


Dependency Injection and Google's Guice framework need no introduction from me, as the documentation on Google's project hosting site is pretty good. Also, there are loads of articles and other information related to these topics, on the internet. So, I will start off with a pretty basic java project, to show 'Guice' in action.

Okay...Now lets think of a very small framework/project (so that, it will be easy to understand) to calculate the sum of all consecutive integers from 1 to n. You are given 'n' as the input and you are supposed to output the sum of all consecutive integers from 1 to n. Lets imagine that there is only one implementation, that we know of (at that moment), to implement the solution...but lets stick to a good design procedure of object oriented languages and make it flexible for future changes.

Below is the code for the only implementation that I know of, at that moment:

IMathAdditionService:

package com.chetty.service;

public interface IMathAdditionService {
public int sumOfAllConsecutiveNumbers(int n);
}



SnailAdditionService (implements IMathAdditionService):

package com.chetty.service;

public class SnailAdditionService implements IMathAdditionService {
@Override
public int sumOfAllConsecutiveNumbers(int n) {
int sum = 0;
long startTime = System.nanoTime();

for(int i=1;i<=n;i++) {
sum += i;
}

long endTime = System.nanoTime();

System.out.println("Snail Addition Service - Sum : " + sum);
System.out.println("Time Complexity - O(n) and Time Taken : " + (endTime - startTime));
return sum;
}
}
Okay, now lets create an interface/client for this service (NOTE: I assume, you know something about 'Factory Pattern'. If not, check it here!). If you implement the 'Factory Pattern', this is how the code looks like: MathServiceFactory:
package com.chetty.service;

public class MathServiceFactory {
private MathServiceFactory() {}
private static IMathAdditionService mathService = new SnailAdditionService();

public static IMathAdditionService getInstance() {
return mathService;
}

public static void setInstance(IMathAdditionService service) {
mathService = service;
}
}

MathClient:
package com.chetty.client;

import com.chetty.service.IMathAdditionService;
import com.chetty.service.MathServiceFactory;

public class MathClient {
public void go() {
IMathAdditionService mathService = MathServiceFactory.getInstance();
mathService.sumOfAllConsecutiveNumbers(1000);
}
}
Implementing the factory pattern makes it a bit loosely coupled, but doesn't really take off the dependencies. Also, you would have to provide different factories for new/different implementations. This is where 'Dependency Injection' comes in handy and Google's Guice is one of the frameworks created to provide DI techniques. Below is the code, which uses 'Constructor Injection' and the Guice's 'Inject' annotation, to inject the required service, at runtime. This kind of approach is more flexible, has less code and you can bind/implement new services (or service providers) just by a minimal change in code/configuration. For the same reason, it is also easier to test the code. 'Plug n Play', 'Service Oriented Arhcitecture (SOA)', 'Webservices' come to mind, when you think about this kind of loosely coupled stuff. Below is the implementation of the client using Guice:
package com.chetty.client;

import com.chetty.service.IMathAdditionService;
import com.google.inject.Inject;

public class MathClient {
private final IMathAdditionService mathService;

@Inject
public MathClient(IMathAdditionService mathService) {
this.mathService = mathService;
}

public int sumOfAllConsecutiveNumbers(int n) {
return mathService.sumOfAllConsecutiveNumbers(n);
}
}

Apart from this, you would have to create a module (which provides/replaces the factory class implementation): MathModule:
package com.chetty.module;

import com.chetty.service.IMathAdditionService;
import com.chetty.service.SnailAdditionService;
import com.google.inject.AbstractModule;

public class MathModule extends AbstractModule {
@Override
protected void configure() {
bind(IMathAdditionService.class).to(SnailAdditionService.class);
}
}

As you see in the code above, this is the class where the required service is configured for use, by the client. In the future, if you have a new implementation for the client, all you have to do is to change the configuration. This technique sticks better to the 'Open Closed Principle', than the factory implementation. And below is the main class, where you use the Guice's injector:
package com.chetty.client;

import com.chetty.module.MathModule;
import com.chetty.service.IMathAdditionService;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class MathApp {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new MathModule());

MathClient mathClient = injector.getInstance(MathClient .class);

mathClient.sumOfAllConsecutiveNumbers(1000);
}
}
Ok...At some point later, I got to know a new algorithm/technique to find the sum of all consecutive integers from 1 to n. So, I want to hook up this new solution to the client, which is faster and has less code. Below is the code for that: FastAdditionService:
package com.chetty.service;

public class FastAdditionService implements IMathAdditionService {
@Override
public int sumOfAllConsecutiveNumbers(int n) {
long startTime = System.nanoTime();
int sum = (n * (n+1))/2;
long endTime = System.nanoTime();

System.out.println("Fast Addition Service - Sum : " + sum);
System.out.println("Time Complexity - O(1) and Time Taken : " + (endTime - startTime));
return sum;
}
}
All that you have to do now, is to extend the framework (but no modifications) with this new service and change the configuration in the module class (MathModule.java), as below:
bind(IMathAdditionService.class).to(FastAdditionService.class);


Looking at the Module class used for configuration, you might be wondering why we are providing configuration in a Java class, instead of the usual way of using XML or bundles/properties files.....Well, it has a reason...The reason is to provide a tight binding (for configuration) and catch all errors at compile time, so that there will be no untoward exceptions/errors on production, at runtime.

Blog Categories
Disclaimer
The views expressed on this blog are my personal views and do not reflect the views of my employer or campaigns I am supporting.

All sample code is provided for illustrative purposes only. These examples have not been thoroughly tested under all conditions. The writer therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.

All programs contained herein are provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.