Recently I placed a comment on this interesting blog from Uncle Bob Martin (Robert C. Martin). It contains a brief description on how I teach people how to use the Spring Framework.

Now, by popular demand (one person requested it over Twitter), I’ll guide you through the method and examples I use in this blogpost. It explains why people should use frameworks like Spring and/or Google Guice, and how.

Ordering Milk

Lets take a look at the application we have. We have a service:

package nl.redcode.examples.milkman;

public interface MilkRequestService {

	/**
	 * Process a new order.
	 * @param customer
	 * @param amountOfBottles
	 */
	public void processOrder(String customer, Integer amountOfBottles);

}

And we have a DAO (data access object) which will take care of persisting our order:

package nl.redcode.examples.milkman;

public interface MilkDAO {

	/**
	 * Save a new order.
	 * @param customer
	 * @param amountOfBottles
	 */
	public void saveOrder(String customer, Integer amountOfBottles);
}

The implementation of this DAO isn’t really interesting, lets just pretent it goes to the database:

package nl.redcode.examples.milkman;

public class MilkDAODatabaseImpl implements MilkDAO {

	public void saveOrder(String customer, Integer amountOfBottles) {
		//Puts order in database
	}
	
}

And finally we have our service implementation:

package nl.redcode.examples.milkman;

public class MilkRequestServiceImpl implements MilkRequestService {

	private MilkDAO milkDAO;
	
	/**
	 * Create a new service:
	 */
	public MilkRequestServiceImpl() {
		milkDAO = new MilkDAODatabaseImpl(
				//With SQLConnection or something
				);
	}
	
	/**
	 * Place a new order for milk.
	 */
	public void processOrder(String customer, Integer amountOfBottles) {
		//Log the order:
		System.out.println("LOG: Customer " + customer + " wants "
				+ amountOfBottles + " bottle"
				+ ((amountOfBottles > 1) ? "s" : ""));
		//Save the order:
		milkDAO.saveOrder(customer, amountOfBottles);
	}
}

Lets pretent that we are good obeying programmers, although we didn’t write our unit test up front, we are going to do it right away.

So… we want to test the service, how are we going to do this? Well, lets just create the service, call it, and then check if it does what we want it to do!

package nl.redcode.examples.milkman;

public class MilkRequestServiceTest {

	MilkRequestService milkRequestService = new MilkRequestServiceImpl();
	
	@Test
	/**
	 * Test our milk request service.
	 * Pre:
	 * - We have a customer that wants to order 5 bottles of milk
	 * Post:
	 * - The milk DAO got a request to save 5 bottles of milk
	 */
	public void testProcessOrder() {
		
		final String customer = "testUser";
		final Integer amountOfBottles = 5;
		
		milkRequestService.processOrder(customer, amountOfBottles);
		
		//Eeehh.... how do we check the call to the DAO?
		
		//HELP!!
	}
}

We run into a problem rather quickly. Now this code makes a connection to the database and it might or might not save our order. But how do we check this? We could go to the database and check…

NO! Never go to the database in a unit test. We only want to test a single unit of work, and the database ain’t one of them.

We need to refactor our code…!

The Factory

One possible solution to this problem is using a factory method. A factory creates objects so you don’t have to. Its best shown using an example:

package nl.redcode.examples.milkman;

import java.util.HashMap;
import java.util.Map;

public class MilkFactory { //no pun intended
	
	/**
	 * Here we save the mapping:
	 */
	private static Map<Class, Object> mapping;
	
	/**
	 * Create the mapping
	 */
	static {
		mapping = new HashMap<Class, Object>();
		mapping.put(MilkDAO.class, new MilkDAODatabaseImpl());
		mapping.put(MilkRequestService.class, new MilkDAODatabaseImpl());
	}
	
	/**
	 * Set a mapping by hand.
	 * Should only be used when unit testing.
	 * 
	 * @param <T>
	 * @param clazz
	 * @param impl
	 */
	public static<T> void replaceMapping(Class<T> clazz, T impl) {
		mapping.put(clazz, impl);
	}

	/**
	 * Not threadsafe ugly code-monkey code.
	 * @param <T>
	 * @param requestedClass
	 * @return
	 */
	public static<T> T get(Class<T> requestedClass) {
		if(mapping.containsKey(requestedClass)) {
			return (T)mapping.get(requestedClass);
		} else {
			throw new IllegalArgumentException("Doesn't exist");
		}
	}
}

So, lets dissect this.

The method get(class, object) will return an implementation for a given Class. It uses a Map to get the mapping. The map is created during the static initialization (yuk!) and contains the default mapping, but we can also provide our own mapping using replaceMapping(class, object).

Now how do we use it? Lets see our new MilkRequestServiceImpl:

package nl.redcode.examples.milkman;

public class MilkRequestServiceImpl implements MilkRequestService {

	/**
	 * Place a new order for milk.
	 */
	public void processOrder(String customer, Integer amountOfBottles) {
		//Log the order:
		System.out.println("LOG: Customer " + customer + " wants "
				+ amountOfBottles + " bottle"
				+ ((amountOfBottles > 1) ? "s" : ""));
		
		//Save the order:
		MilkFactory.get(MilkDAO.class).saveOrder(customer, amountOfBottles);
	}
}

As you can see we no longer need the constructor, we get the required DAO from the factory when we need it.

Now we can test it using the replaceMapping method:

package nl.redcode.examples.milkman;

public class MilkRequestServiceTest {

	MilkRequestService milkRequestService = new MilkRequestServiceImpl();
	
	//@Test
	/**
	 * Test our milk request service.
	 * Pre:
	 * - We have a customer that wants to order 5 bottles of milk
	 * Post:
	 * - The milk DAO got a request to save 5 bottles of milk
	 */
	public void testProcessOrder() {
		
		final String customer = "testUser";
		final Integer amountOfBottles = 5;
		
		//Better use a mock here, for example using Mockito or EasyMock.
		MilkFactory.replaceMapping(MilkDAO.class, new MilkDAO() {
			public void saveOrder(String customer, Integer amountOfBottles) {
				//Check if the customer = testUser
				//Check if the amountOfBottles = 5;
			}
		});
		
		MilkFactory.get(MilkRequestService.class)
			.processOrder(customer, amountOfBottles);
	}
}

As the comment says, you are probably much better of here using a mocking framework like EasyMock or Mockito. But the point is, we can test our class now without going to the database! We got our control back.

Dependency Injection

Instead of writing a factory, there is a better way to do this. It is the Hollywood principle “Don’t call us, we’ll call you”.

All the dependencies of the objects are injected into them instead of created (initial example) or retrieved (factory example). So our service will look like this:

package nl.redcode.examples.milkman;

public class MilkRequestServiceImpl implements MilkRequestService {

	private MilkDAO milkDao;
	
	/**
	 * The milkDao is given to us by our creator.
	 * 
	 * @param milkDao
	 */
	public MilkRequestServiceImpl(MilkDAO milkDao) {
		this.milkDao = milkDao;
	}
	
	/**
	 * Place a new order for milk.
	 */
	public void processOrder(String customer, Integer amountOfBottles) {
		//Log the order:
		System.out.println("LOG: Customer " + customer + " wants "
				+ amountOfBottles + " bottle"
				+ ((amountOfBottles > 1) ? "s" : ""));
		
		//We just use the milkDao we got:
		milkDao.saveOrder(customer, amountOfBottles);
	}
}

Now lets take a look at our test-code, it now looks like this:

	public void testProcessOrder() {
		
		final String customer = "testUser";
		final Integer amountOfBottles = 5;
		
		//Better use a mock here, for example using Mockito or EasyMock.
		MilkDAO testMilkDao = new MilkDAO() {
			public void saveOrder(String customer, Integer amountOfBottles) {
				//Check if the customer = testUser
				//Check if the amountOfBottles = 5;
			}
		};
		
		MilkRequestService milkRequestService = new MilkRequestServiceImpl(testMilkDao);
		
		milkRequestService.processOrder(customer, amountOfBottles);
	}

We now have full control over the wiring. First we create our own little testMilkDao (use a mock here!) and we place it into our service. That’s all. Instead of the hidden creation in the service itself or in the factory we are in full control.

But if you want to use this application you’ll need “something else” to create all the objects and do the actual wiring. Lets create our own Spring or Google Guice..!!

package nl.redcode.examples.milkman;

public class MilkApplication {

	public static void main(String[] args) {
		MilkApplication app = new MilkApplication();
		app.run();
	}

	public MilkApplication() {
		//-----   Provide wiring:  -----
		MilkDAO milkDao = new MilkDAODatabaseImpl();
		milkRequestService = new MilkRequestServiceImpl(milkDao);
		//------------------------------
	}
	
	private MilkRequestService milkRequestService;
	
	/**
	 * Do stuff:
	 */
	public void run() {
		milkRequestService.processOrder("Roy van Rijn", 1);
	}
}

Using Spring

The wiring we do in the constructor is what Spring and Google Guice would do for us. In the case of Spring you’ll write something like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="milkDao" class="nl.redcode.examples.milkman.MilkDAODatabaseImpl" />

	<bean id="milkRequestService" class="nl.redcode.examples.milkman.MilkRequestServiceImpl">
		<property name="milkDao" ref="milkDao" />
	</bean>
</beans>

Now you’ll still need to retrieve these “beans” from Spring, this can be done using our old-friend the Factory!
It’ll look something like this:

	ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
		new String[] {"applicationContext.xml"});
	MilkRequestService milkRequestService = appContext.getBean("milkRequestService");

This instructs Spring to load the XML file and create all the objects. Then we retrieve the milkRequestService and it is fully wired and ready to use!

Using Guice

Google Guice works exacly the same, only instead of using XML to define the mapping it only uses Java code and annotations.

First we tell Guice which implementations are the default implementations, for example:

package nl.redcode.examples.milkman;

@ImplementedBy(MilkDAODatabaseImpl.class)
public interface MilkDAO {

	/**
	 * Save a new order.
	 * @param customer
	 * @param amountOfBottles
	 */
	public void saveOrder(String customer, Integer amountOfBottles);
}

We also do this to the MilkRequestService. Next we tell Guice where to inject these implementations, in this case using ‘Constructor Injection’:

	/**
	 * The milkDao is injected by Guice.
	 * 
	 * @param milkDao
	 */
	@Inject
	public MilkRequestServiceImpl(MilkDAO milkDao) {
		this.milkDao = milkDao;
	}

Now if we want to use it, we retrieve it from Guice like so:

public class MilkApplication {

	public static void main(String[] args) {
		MilkApplication app = new MilkApplication();
		app.run();
	}
	
	/**
	 * Do stuff:
	 */
	public void run() {
		
	    Injector injector = Guice.createInjector();
	    MilkRequestService milkRequestService = 
	        injector.getInstance(MilkRequestService.class);

		milkRequestService.processOrder("Roy van Rijn", 1);
	}
}

Thats about it, you’ve seen:

  • Why you should use these patterns
  • How you would program them yourself (ugly monkey-code-style)
  • How using Spring (with XML)
  • How using Google Guice!

Good luck and happy programming!

ps. The code examples are just examples…!!

Tagged with:
 

One Response to Learn to use Dependency Injection

  1. Jon says:

    Thanks for the article, but enlarge the font on the code illustrations. Two times I had to press “Ctl+” just to be able to read it in my browser.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>