binding
I've been working on the Zero team for almost a year now, and in that time, Groovy has become my language of choice, both for Zero applications and non-Zero utilities. Groovy is, as Jerry Cuomo put it, "the nicotine patch for Java programmers"; it provides many of the cool features found in Python while freeing me from the tedious boilerplate of Java, all with a gentle learning curve. Like most Java-turned-Groovy users, I started out writing Java-centric code, picking up Groovy's shortcuts and elegance as I grew more experienced and shared code bases with other Groovy users. There are still many features that are not part of my toolbelt, but every day I seem to pick up a new one.
Because I use Groovy both for RESTful resource implementations and utility scripts, I often use Zero's /app/scripts directory to store code that is in any way reusable; this shortens my resource scripts and keeps time spent refactoring to a minimum. The only problem with invoking code in /app/scripts is that you have to do so with generic, reflection-based APIs, like so:
To make it so the code in /app/scripts is in scope for your other Groovy code, you need to create a binding. Making a Groovy binding for a script isn't hard, it's just kind of tedious: you write a Java class that maps all standalone function names to reflection-based invocations on the Java class, and then use Groovy's script engine API to call the target function. You must also update your configuration file to register your Java class as a Groovy binding. The whole process is outlined in Zero's documentation as well as every developerWorks article I've written in the last six months. If you follow the instructions prescribed by the Zero team, the block of code shown above will become much more readable:def script = "FooUtils.groovy";
def method = "getFoo";
def params = ["param1", "param2", ...];
def foo = invokeMethod(script, method, params);
def foo = getFoo("param1", "param2", ...);
It's not often that I put code in /app/scripts that isn't meant to be shared with the rest of my application, so after I while I started poking around zero.core to see if there was a way to enable bindings automatically, with no Java code or config stanzas. The short answer is that, yes, it would be possible, but we would take a performance hit because of some additional reflection; I have not bothered to implement this solution, so I cannot say how severe this performance hit would be. I didn't want to go through a lot of trouble only to find out that my solution was slow as molasses, so instead I wrote a Groovy script to generate the binding classes and config stanzas for me.The script is named binding.groovy, and you can download it here. You can look at a sample console session below:
The script generates classes and configuration without touching any of your existing files. The zero build step compiles the Java classes so that they will be on the classpath at run time (zero run). You can find more details on usage, behavior, and licensing in the header comments.$ ls
.
..
binding.groovy
my.zero.app
$ groovy binding my.zero.app
$ zero build
$ zero run
Working on this tool gave me the opportunity to make a very useful comparison between Groovy and Java. Last summer I used Java to write RESTdoc, and that tool shares many requirements and behaviors with my latest one: both analyze the structure and code in a Zero application and use that information to generate one or more files using a template. RESTdoc is more complex because it must be usable from the command line, Ant scripts, and a GUI, but many of the algorithms are the same.
Based on my two experiences, I would have to say that using Groovy was far more enjoyable than using Java. But why?
First, I was able to get right to coding, without having to create all of the boilerplate that seems to appear in all of my non-web applications. You know: first write main(), then a non-static run(), then set up the exception handling, then define an exception hierarchy, and so on; then, just as you're starting to write real code, your mind starts to map out the larger pieces of the tool, and you start to think about which of these pieces should be pluggable, and then you start defining interfaces, and soon it's the end of the day and all you've done is create an Architecture.
Tomorrow, you think, I just have to write the code. And it seems like such a logical thought to have.
But it's not.
I wrote RESTdoc in just over two days. This latest tool required four hours. Granted, I was able to reuse many of the ideas I'd had while implementing RESTdoc, but those are just ideas - I couldn't reuse most of the code because it was all so... big. I knew that the code could be much simpler in Groovy, so I rewrote it. Quickly. The Groovy tool took less time because I was able to focus on actual logic and actual testing, not Java-oriented procedures that catered to my neuroses.
Second, the ability to use closures made my code smaller while also increasing its readability. Most of my closure usage is coupled with methods like each() and collect() (and their derivatives), methods that accept a closure as a parameter and apply it to a collection. I'm sure that some people abuse closures in a way that makes them feel like Java's anonymous classes, but for the most part they seem to function as a way to get things done with less bureaucracy.
The third thing that makes my Groovy development more enjoyable is the fact that lists (java.util.List) and maps (java.util.Map) are built into the language, and I can use them to create utility data structures without defining an inner class with getter and setter methods. You can do this in Java too, but it's frowned upon; it just doesn't feel right to put so much structure around your code and then use bags of goo to store your data. But while that's an appropriate feeling to have in many scenarios, it's a real downer when you're writing a script to generate config files. I love the fact that I can represent part of a parse tree with a set of key-value pairs and not feel guilty about it, and I really love the fact that I can create that set in one line of code:
Finally, for all of the Java bashing I've done in this post, I have to remind myself that one of the best things about Groovy is the fact that it lets me devolve into traditional Java programming when I really need it. There are some tools for which Java integration is superior, and those tools aren't going to change any time soon. Java is also the original language of the JVM, and it is the best way to expose a language-agnostic API on that platform.return [
name: "getFoo",
params: ["param1", "param2"],
hasReturnValue: true
];
And sometimes, I'm just not ready to do things The Groovy Way. Like all creatures of habit, there are times when I hang on desperately to the past, for no good reason at all. Groovy allows for all of that, and it doesn't mock me when I fail to use it to the best of its abilities. It just runs my script.
He'll come around, it says to itself. Some day.
Labels: narcissism, programming, zero
0 Comments:
Post a Comment
<< Home