A coworker recently pointed out that I have a tendency to go on "kicks" where I form some opinion about design or programming style, apply it enthusiastically, and end up taking it too far. "We all do it", he says, although I think I may suffer from this syndrome more than most.
We fall into these mentalities individually, but also sometimes as a group. One design kick several of us been on for a while now is favoring composition over inheritance. I think Java's extends keyword is appropriate in some circumstances, but it is far overused.
For a specific example, consider GWT's Timer class. To create a Timer, you have to extend Timer and implement its abstract void run() method.
// Create a timer.
Timer t = new Timer() {
@Override public void run() {
Window.alert("Time has run out."); } };
// Schedule the timer to run in 60 seconds.
t.schedule(60000);
What happens when you try to write a unit test for a class that uses Timer? You don't want to actually wait 60 seconds for a real Timer to fire (and Timer's JSNI implementation probably doesn't work in your unit test environment anyway).
If you're into dependency injection, the standard solution to this type of problem is a quick refactor. Construct the Timer outside of the object being tested, inject it as a constructor parameter, and let the test case inject a mock. But this is impossible, because Timer's design mandates that it must be constructed at the same place where its run behavior is defined - inside the object being tested.
To build a compositional Timer, you need to use an interface like Runnable to specify the timer's run behavior.
public class CompositionalTimer extends Timer {
private final Runnable task;
public CompositionalTimer(Runnable task) {
this.task = task; }
@Override public void run() { task.run(); }
}
If we were to put a mutator on the task field instead of setting it in the constructor, we could just inject a CompositionalTimer and be done with it. Personally, I find that final fields make for much more understandable code, so I would introduce a factory to inject.
public class CompositionalTimerFactory {
public CompositionalTimer constructTimer(
Runnable task) {
return new CompositionalTimer(task); } }
Some complexity has been added to our use case, but not much.
// Create a timer.
Timer t = timerFactory.constructTimer(
new Runnable() {
@Override public void run() {
Window.alert("Time has run out."); } });