Concepts

Interfaces are Wonderful Things (part 1)

When writing a simple application, where we know exactly what we want it to do, and don’t imagine the scope changing, it is very easy to simply write code and not worry about making it readable/generic/easily modifiable. However in practice, this is rarely the case and applications constantly need to be changed/grown – and often by people who did not write the original code.

Let’s have a look at how interfaces can help:

1) They allow us to independently test different parts of the application
When trying to write a piece of software with several components we need to think carefully about how to test it. We need to test the functionality actually works without muddling the concepts and testing too much in one go. We also want the separate bits to remain decoupled from one another, so that we can happily make changes without worrying about breaking a bunch of seemingly unrelated tests.

Say we have a class which gets some data from a database, and does something with that data. For example, suppose I’m a witch (call me Romilda) and I’d like to send a potion to a Wizard (call him Ron), based on a recipe in a RecipeBook database. I could write the following:

public class PotionService {
	private RecipeBookDAO recipeBook;

	public PotionService(RecipeBookDAO recipeBook) {
		this.recipeBook = recipeBook;
	}

	public void createAndSend(String potionType, Wizard recipient) {
		try {
			Potion potion = createPotion(potionType);
			potion.sendTo(recipient);
		} catch (InvalidPotionException e) {
			System.out.println("Tough luck Roonil Wazlib, potion does not exist");
		}
	}

	private Potion createPotion(String potionType) throws InvalidPotionException {
		if (recipeBook.recipeExistsFor(potionType)) {
			Recipe recipe = recipeBook.getRecipeFor(potionType);
			return new Potion(recipe);
		}
		throw InvalidPotionException;
	}
}

public class RecipeBookDAO {
	public boolean recipeExistsFor(String potionType) {
		// Hit database
	}

	public Recipe getRecipeFor(String potionType) {
		// Hit database
	}
}

public static void main (String[] args) {
    // halfBloodPrinceDataSource setup
    // ...
    RecipeBookDAO halfBloodPrinceRecipeBook = new RecipeBookDAO(halfBloodPrinceDataSource)
	PotionService potionService = new PotionService(halfBloodPrince);
	Wizard ron = new Wizard("Ron Weasley");

	potionService.createAndSend("Love Potion", ron);
}

There are a couple of problems here though.

Firstly, we have to decide which recipe book we want to use from the beginning. If Flourish and Blotts then publish a new version of Advanced Potion Making then we will have to go in to the class itself and change this up. What if we now want to use Slughorn’s book?

Secondly, and perhaps more importantly, how would we test PotionService? At the moment, we would be forced to call the RecipeBookDAO and in turn the RecipeBook, and if it’s been confiscated by a certain Potions Master this wouldn’t really work. It doesn’t really make sense either – why should our test to create a potion rely on a RecipeBook? Potions and books should not be coupled so tightly – they should be tested entirely separately.

So how do we solve this conundrum? (Hint: the title might’ve given it away…)

That’s right! Create an interface, whip out your wand, point it at the test, say “Flipendo” and as if by magic, everything works! I kid, there’s a very good reason behind this:

Right now, our PotionService class hits an actual predefined RecipeBookDAO object (The Half Blood Prince’s), which calls that database (HBPRecipeBook). Therefore, we have no choice but to call the database every time we run the methods in the PotionService.

Instead, we should get our PotionService to call an Interface which we can then implement in different ways depending on how we want it to be used. This can solve both our problems:

1) When running the production code, we can set the implementation to be the HalfBloodPrince version of RecipeBook (or any other implementation of the recipe book depending on what we need).

2) When running tests, we can create a recipeBookStub, and mock the response, without ever touching the database. This way we can also ensure that whenever the method is called on that implementation of the RecipeBookDAO, it returns the value we need it to for the test to work. Neat, huh?

The code would now look like this:

public class PotionService {
	private RecipeBook recipeBook; //Using interface instead of implementation

	public PotionService(RecipeBook recipeBook) {
		this.recipeBook = recipeBook;
	}

	public void createAndSend(String potionType, Wizard recipient) {
		try {
			Potion potion = createPotion(potionType);
			potion.sendTo(recipient);
		} catch (InvalidPotionException e) {
			System.out.println("Tough luck Roonil Wazlib, potion does not exist.");
		}
	}

	private Potion createPotion(String potionType) throws InvalidPotionException {
		if (recipeBook.containsRecipeFor(potionType)) {
			Recipe recipe = recipeBook.getRecipeFor(potionType);
			return new Potion(recipe);
		}
		throw InvalidPotionException;
	}

	private boolean recipeIsAvailableFor(String potionType) {
		return recipeBook.recipeExistsFor(potionType);
	}
}

public interface RecipeBook {
	boolean recipeExistsFor(String potionType);
	Recipe getRecipeFor(String potionType);
}	

public class RecipeBookDAO implements RecipeBook {
	@Override
	public boolean recipeExistsFor(String potionType) {
		// Hit database
	}

	@Override
	public Recipe getRecipeFor(String potionType) {
		// Hit database
	}
}

public static void main (String[] args) {
    // halfBloodPrinceDataSource setup
    // ...
    RecipeBook halfBloodPrinceRecipeBook = new RecipeBookDAO(halfBloodPrinceDataSource)
	PotionService potionService = new PotionService(halfBloodPrince);
	Wizard ron = new Wizard("Ron Weasley");

	potionService.createAndSend("Love Potion", ron);
}

This overall gives us much more flexibility when writing tests, and stops our tests from breaking for silly reasons :).

>>>> Interfaces: Part 2

Advertisements

One thought on “Interfaces are Wonderful Things (part 1)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s