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);
}
}