Juicy Code with Google Guice - Part 3

In the previous part, I created a sample application to test-drive Google Guice. I used the factory functionality of the framework. Now, let’s extend our example to include a few dependency injections! Let’s see how this works in Google Guice!

First, to take the example a little further I’m going to include two new methods, called defend() and sustainDamage(), to the Character interface:

package dsi.guice.rpg;

public interface Character {

public int attack();
public int defend();

public int causeDamage();
public void sustainDamage(int theDamage);



}

As the ElvenRanger class implements the Character interface, I need to create the implementation of the defend() method in ElvenRanger as well. To make the example a little bit realistic I’m going to add a concept called base armor. I’m going to consider that all characters have a base armor value depending on their races. Furthermore, I’m going to add a property called “Dexterity” to the Character class. The value of a character’s dexterity is going to be added to its defense rating:

public class ElvenRanger implements Character {


private static final int BASE_ARMOR = 10;
private static final int DEFAULT_DEXTERITY = 4;

private int dexterity = ElvenRanger.DEFAULT_DEXTERITY;

public int defend() {
return ElvenRanger.BASE_ARMOR + getDexterity();
}

public void setDexterity(int dexterity) {
this.dexterity = dexterity;
}

public int getDexterity() {
return dexterity;
}

public void sustainDamage(int theDamage) {
System.out.println(name + ” sustains damage(” + theDamage + “)”);
if (theDamage > 0) {
hitPoints = hitPoints - theDamage;
if (hitPoints < 1) {
System.out.println(name + ” goes down!”);
listener.characterDown(this);
}
}
}

}

I’m going to introduce a new class called Arena. Arena will hold references to two Characters. This is where we will be able to inject Character implementations into the Arena for the fight. Calling the startFighting() method will start consecutive attack() & defend() method invocations on the Characters until one of them sees his/her hit point equal to zero or below. The two most important methods for our example are the two setters that are going to allow me to inject the Characters fighting in the Arena:

package dsi.guice.rpg;

import com.google.inject.Inject;

public class Arena implements FightLocation {

private Character fighter1;
private Character fighter2;



@Inject
public void setFighter1(Character theChar) {
fighter1 = theChar;
fighter1.setCharacterListener(this);
}

@Inject
public void setFighter2(Character theChar) {
fighter2 = theChar;
fighter2.setCharacterListener(this);
}


public void startFighting() {
while (!fightEnded) {
runFightRound();
}
}

}

You’ve probably noticed the @Inject annotations immediately. That’s how Google Guice injects the required objects (it’s like the Spring Autowiring byType). Google Guice uses the binding specified in the Module class to resolve the dependency and inject the correct implementation. My Module class looks like this:

package dsi.guice.rpg;

import com.google.inject.AbstractModule;
import com.google.inject.Scopes;

public class CharacterModule extends AbstractModule {

public void configure() {
bind(Character.class).to(ElvenRanger.class);
bind(FightLocation.class).to(Arena.class).in(Scopes.SINGLETON);
}

}

The Module above defines Arena class as the implementation of the FightLocation interface and the system is going to use a singleton Arena instance. It also defines ElvenRanger class as the implementation of the Character interface. However I omitted the Singleton Scope instruction because I wanted to create two distinct ElvenRanger objects. Feel free to run the code with and without the in(Scopes.SINGLETON) invocation. You’ll see the difference.

We’re not going to worry about which fighter is attacking first. I’m going to assume that Fighter1 always attacks first.

My App class is pretty simple as well:

package dsi.guice.rpg;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class App {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new CharacterModule());
FightLocation arena = injector.getInstance(FightLocation.class);
arena.startFighting();
}
}

When you run this example, you’ll see that the bindings and the injections happen successfully. The only thing that bothers me so far is the use of @Inject annotation in the domain model. This creates a dependency to the Google API in my domain model and I don’t like this. Am I too purist? I’m not sure if I can call this being purist but to me a POJO shouldn’t have dependencies to frameworks.

In the next part of this article I’m going to look at the @ImplementedBy annotation, annotating bindings and probably the custom providers.

Technorati Tags: , ,

0 Responses to “Juicy Code with Google Guice - Part 3”


  1. No Comments

Leave a Reply