Don’t look for things. Ask for them!
November 26, 2009 at 12:04 pm | blog, software quality | 3 comments
Before our previous article, our code to model a mechanic used to look like this:
class Mechanic {
private final Engine engine = ServiceLocator.getCar().getEngine();
public void fixEngine() { /* ... */ }
}
Luckily, we applied some dependency injection, resulting in nicer code: testable, and with an explicit dependency on the ServiceLocator:
class Mechanic {
private final Engine engine;
public Mechanic(final ServiceLocator serviceLocator) {
engine = serviceLocator.getCar().getEngine();
}
public void fixEngine() { /* ... */ }
}
However, does our mechanic care about the ServiceLocator at all? Not really, it doesn’t even store a reference to it! The mechanic just wants an engine.
There are some problems with this kind of code:
- The
ServiceLocatorprobably has references to a lot of other classes in the system. By the transitive property, ourMechanicclass is too coupled with the rest of the system: if you want to reuse it in a different project, you’d need to reference not onlyMechanicandEngine, but also theServiceLocatorwith all its dependencies. - The API is not clear. An API client knows that it’d need to provide the
ServiceLocator(already an advantage respect to the Singleton-based case). What it doesn’t know is what the mechanic really needs (an engine). This fact is hidden in the source code. - The tests contain a lot of setup junk that masks its real purpose. For every test of the mechanic, you’d need to mock the ServiceLocator, and then make sure the appropriate reference can be retrieved from it (the
Engine, in this case). When we test the classGarage, that needs aMechanic, we’ll have to replicate this tedious setup there too.
This is how the test looks like now:
@Test
public void testCantFixBrokenDownEngine() {
final Engine engine = EngineFactory.buildBrokenEngine();
final Car mockCark = Mockito.mock(Car.class);
Mockito.when(mockCar.getEngine()).thenReturn(engine);
final ServiceLocator mockServiceLocator = Mockito.mock(ServiceLocator.class);
Mockito.when(mockServiceLocator.getCar()).thenReturn(car);
final Mechanic mechanic = new Mechanic(mockServiceLocator);
mechanic.fixEngine();
assertFalse(engine.works());
}
Why not just ask for what you need?
class Mechanic {
private final Engine engine;
public Mechanic(final Engine engine) {
this.engine = engine;
}
public void fixEngine() { /* ... */ }
}
@Test
public void testCantFixBrokenDownEngine() {
Engine engine = EngineFactory.buildBrokenEngine();
Mechanic mechanic = new Mechanic(engine);
mechanic.fixEngine();
assertFalse(engine.works());
}
Everyone wins:
API writers:
- Unaffected by ServiceLocator changes
- In-code documentation is easier to write (what was your comment on the
Mechanic‘s constructor for theServiceLocator)?
Test writers:
- Tests are easy to read
- Very little setup code
- Unaffected by
ServiceLocatorchanges
API users:
- The API is clear: the mechanic needs an engine

En espaƱol?
Interesting blog you got here but I can’t seem to find the RSS button.
It’s “hidden” in the orange image in the top-right corner: http://www.androidsx.com/feed/.
By the way, if you use Chrome, check out this extension, that finds and indicates what the RSS is for the blog you’re viewing: https://chrome.google.com/extensions/detail/nlbjncdgjeocebhnmkbbbdekmmmcbfjd. For Firefox this is built-in.
I Hope Everyone doesent Underevaluate the true Importance of what you have posted here…Please guys dont skim read this take time and read into thouroughly….Thumps up to the Writer…Great work!