views. I’ve had success with logic-less template languages such as Mustache for build- ing simple, readable views for web applications. The templates can be written and maintained by web designers, allowing the developers to focus on the business logic.
APPLICABILITY TO OTHER LANGUAGES The View Adapter pattern is not specific to Java and JSP templates. It’s useful no matter whether you’re using Ruby and ERB, ASP.NET, or any other technology. Whenever you have an application with a UI of some kind, you can and should keep complex logic out of the view layer.
4.3
Testing legacy code
When refactoring legacy code, automated tests can provide valuable assurances that the refactoring has not inadvertently affected the behavior of the software. In this sec- tion I’ll talk about how to write these automated tests, and what to do when you’re faced with untestable code.
4.3.1 Testing untestable code
Before you start refactoring, you want to have unit tests in place. But before you can write unit tests, you need to refactor the code to make it testable. But before you start refactoring, you want to have unit tests in place ...
Further reading
I’ve just scratched the surface in this brief foray into refactoring. If you’d like to learn more about refactoring, there are plenty of excellent books dedicated to the subject. Here are three recommendations.
Refactoring: Improving the Design of Existing Code by Martin Fowler et al. (Addison-Wesley Professional, 1999). Although it’s getting a little dated (it was written when Java was at version 1.2), it’s a classic and still a great refer- ence. It takes a pattern-based approach, describing in what situation you might want to use a particular refactoring.
Refactoring to Patterns by Joshua Kerievsky (Addison-Wesley Professional, 2004). This book cleverly shows how you can take legacy code that lacks a clear structure, and migrate it toward well-known design patterns using the refactorings described in Martin Fowler’s book. If you want to brush up on design patterns before reading this book, read Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley Professional, 1994), also known as the Gang of Four book.
Principle-Based Refactoring by Steve Halladay (Principle Publishing, 2012). This book is full of useful refactoring techniques, but it takes more of a “teach a man to fish” approach, promoting the value of studying the underlying princi- ples of software design rather than slavishly learning dozens of rules by rote.
This chicken-and-egg situation is something we often face when trying to retroac- tively add tests to legacy code. If we insist on having unit tests in place before refactor- ing, then it seems like an unbreakable paradox. But it’s worth remembering that if we can manage to get a few tests in place, we can start refactoring, making the software more testable, allowing us to write more tests, in turn allowing more refactoring, and so on.
Think of it like peeling an orange. At first it seems perfectly round and impenetra- ble, but once you apply a little force to break the skin, the thing practically peels itself. So we need to lower our standards temporarily in order to break the skin and get our first few tests in place. When we do this, we can use code review to make up for the lack of tests.
Our first priority when trying to make code testable is to isolate it from its depen- dencies. We want to replace all of the objects that the code interacts with, with objects that we control. That way we can feed it whatever inputs we like and measure its response, whether it consists of returning a value or calling methods on other objects.
Let’s look at an example of how we can refactor a piece of legacy code in order to get it into a test harness. Imagine we want to write tests for the Battle class, which is used when two players fight each other in World of RuneQuest. Unfortunately, Battle is riddled with dependencies on a so-called “God class,” a 3,000-line monster of a class called Util. This class is filled with static methods that do all kinds of useful things, and it’s referenced from all over the place.
BEWARE THE UTIL Whenever you see a class with Util in the name, alarm bells should start ringing in your head. It may well be a good candidate for refactoring, if only to rename it to something more meaningful.
Here’s how the code looks before we start.
public class Battle {
private BattleState = new BattleState(); private Player player1, player2;
public Battle(Player player1, Player player2) { this.player1 = player1;
this.player2 = player2; }
...
public void registerHit(Player attacker, Weapon weapon) { Player opponent = getOpponentOf(attacker);
int damageCaused = calculateDamage(opponent, weapon); opponent.setHealth(opponent.getHealth() - damageCaused); Util.updatePlayer(opponent);
updateBattleState(); }
31