Sunday, June 15, 2008

Yet another reason for why Java needs Closures

Ever try to unit test code with static method calls? That's right. You know how this feels.
class  Untestable{
public void doSomething(){
...
if ( Util.openSocketAndThenGoToFileSystem() )
...
}
}
Unit testing doSomething is often impossible because unit tests simply don't do things like talk to networks or file systems. It's even more "fun" when someone puts code like this in a constructor - that way you can't even instantiate an instance of Untestable outside the production environment.

This is particularly painful in Java (as it stands today) for two reasons. First, there are no open classes. Sure, there is conditional compilation or cglib but at the end of the day, it's never going to be as easy as it is in JavaScript, Python, etc.

Second, it calls a static method. If you haven't read Steve Yegge's post on this matter, verbs are second class citizens in Java. Because each verb must be escorted by a noun at all times, methods cannot be stubbed out or mocked. This limitation has profound side effects on the maintainability (and profitability) of every large Java code base.

Enter Closures ...


We can refactor the Untestable class to a Testable class.
class Testable{

private final { => boolean } collaborator;

Testable({ => boolean } collaborator){
this.collaborator = collaborator;
}

public void doSomething(){
...
if ( collaborator.invoke() )
...
}
}
By declaring a closure instance variable we can use Dependency Injection to exercise the functionality of Testable without doing something expensive. This is a lot easier than retrofitting an interface or inner class. Our test methods now look like this:

public void testDoSomething(){
Testable testable = new Testable({ => true });
testable.doSomething();
assertTrue(true, testable.someSideEffect());
}


Neal Gafter tells me the chances of closures making it into the next version of Java is getting smaller and smaller. I'm keeping my fingers crossed, time will tell. Keep up the good work Neal.

3 comments:

Hamlet D'Arcy said...

Is this why Java needs closures or why Java programmers should use dependency injection? I think the latter. And DI is available on all past versions of Java!

Paul Hammant said...

Welcome back to Java, Erlang-boy! ;-)

Neil said...

Why on earth is this easier then retrofitting an inner class or interface?

The call sites still have to be updated, the unit tests have to be updated and the class has to be updated.

Maybe you mean less characters?

In real code the dependency is something that should likely have a well defined role in the system with a real name and well defined by an interface.

There are reasons to have closures in Java. This ain't it.