12/28/2005

Java Closure Chaining

I am playing with closures in java. Not as simple as the Ruby counter parts but a benefit to our programming arsenal I think.

I have come to realize two new things from this experience.

Testability

Compare


Iterator iter = list.iterator();
while (iter.hasNext())
{
A a = (A)iter.next();
do something
}

With

ListTools.each(list, new ClosureObject()
{
public void call(Object a_object)
{
A a = (A)a_object;
do something
}
});

How many pre-bugs do you see in each version. Both have 4 lines of code but the first requires 3 dot references which adds opportunities for those methods to do something unexpected.

Because of the the "final" requirement in java I found this construct to be a rarity. Instead we need something like this.

ListItemMunger munger = new ListItemMunger();
ListTools.each(list, munger);
System.out.println(munger.something());

class ListItemMunger implements ClosureObject()
{
public void call(Object a_object)
{
A a = (A)a_object;
do something
}
});

The most significant benefit with using Closures in java is that the loop-core is removed and is more testable than ever before.

public void testLoopCore()
{
ListItemMunger munger = new ListItemMunger();
munger.call(new SomeObject());
munger.call(new SomeObject());
munger.call(new SomeObject());
assertTrue(munger.something());
}

Now that I have classes that scan directories recursively, scan the files within them and then the line of text within each file this testability feature has been invaluable.

Closure Chaining

So I can scan directories, files and lines within files. How do I do that with these closure things.

DirectoryRecurseLookup.each("my path", new ClosureFile()
{
public void call(File a_directory)
{
FileLookup.each(a_directory, new ClosureFile()
{
public void call(File a_file)
{
StreamLineLookup.each(a_file, new ClosureIntString()
{
public void call(int a_lineNumber, String a_line)
{
System.out.println(a_lineNumber + " : " + a_line);
}
}
}
}
}
});


Well that's a lot of nesting but relatively simple syntacticly, no pre-bugs waiting to pop out at me. My first version took each of the closures into a separate class to simplify the code and make it more testable and it all worked fine.

Then I realized what I was doing wrong. I am just chaining closures so why can't the closures encapsulate that.

StreamLineLookup lineScanner = new StreamLineLookup();
FileLookup fileScanner = new FileLookup(lineScanner);
DirectoryRecurseLookup dirScanner = new DirectoryRecurseLookup(fileScanner);

DirectoryRecurseLookup.each("my path", dirScanner);

So as an example:

class DirecotryRecurseLookup implements ClosureFile
{
public static each(String a_path, ClosureFile a_closure)
{
// find files and pass them to the closure
}

private ClosureFile m_closure;
public DirectoryRecurseLookup(ClosureFile a_closure)
{
m_closure = a_closure;
}

public void call(File a_directory)
{
each(a_directory, m_closure);
}
}