Wednesday, November 12, 2008

You want to know what I think? I'll tell you what I think. Here's what I think: Java Java Java is is is too too too damn damn damn verbose verbose verbose. That's what I think. And I'm sticking to it. So there.

You want to know what I think? I'll tell you what I think. Here's what I think: There's There's There's way way way too too too much much much boilerplate boilerplate boilerplate in in in Java Java Java. That's what I think. And I'm sticking to it. So there.

You want to know what I think? I'll tell you what I think. Here's what I think: If If If Charles Charles Charles Dickens Dickens Dickens had had had designed designed designed a a a language language language, it it it would would would be be be a a a lot lot lot like like like Java Java Java. That's what I think. And I'm sticking to it. So there.

I was writing a small helper class to correctly paste together the CGI arguments to an http service. The idea is simple. Rather than simply appending strings in a kludgy way, you'd collect a number of instances of the objects you want to pass (mostly integers, text, and enums) and the code would magically marshal them in to a correctly escaped URI. No big deal.

Two days later...

Sixteen hundred lines of code distributed in twenty-nine files. Is it any wonder that people get carpal tunnel syndrome? And this is mostly `throwaway' code that abstracts only the small amount of functionality I need. I don't even want to think how awful it would be if I had tried to engineer a full-blown class-based utility.

Of course I was asked “Why didn't you just use the mumble class?” A few reasons, but the primary reason was that the suggested class worked like this:

    FooBuilder fooBuilder = new fooBuilder();

    FooPartBuilder fooPartBuilder = new fooPartBuilder();
    fooPartBuilder.addName ("fooPartName");
    fooPartBuilder.addValue ("fooPartValue");

    FooPart fooPart = fooPartBuilder.createPart();
    fooBuilder.addPart (fooPart);
    .... etc. etc. ...

I wanted something like this:

    Foo myFoo = new Foo (new FooPart ("fooPartName", "fooPartValue"),
                         new BellOption (BellOption.Sound.Tinkle),
                         new WhistleOption (), ...)

What's the difference? As I was skimming the code for FooBuilder (and the code for AbstractFoo, FooInterface, AbstractFooBase, etc...) I saw a note that mentioned ‘this isn't thread safe’. Great. Another thing to think about. The problem with the FooBuilder model is that it is just lousy with state. It starts empty and then you smash some state into it. This is stupid. It begins in a state that no one wants it to be in, so the first thing everyone does is smash the state to something more useful.

They way I wanted to do it was to simply construct the object in its final configuration. The object is immutable and stateless. It is guaranteed to be thread safe because there is no way to modify it. The JVM could keep separate copies all over the place for all I care. And because there are no mutators, the code is about 30% smaller. That's 30% fewer javadoc comments that replicate the argument lists.

Another gripe: Having a database that keeps track of where the source code resides is a good idea. Using the file system as a ‘poor man's database’ for that purpose is a bad idea. To give a concrete example, I wanted to say, formally, that ‘every optional CGI argument must provide a marshaling function’. Simple statement. Here goes:

    public abstract class OptionalCgiArgument {
        abstract String marshal() throws MarshalingException;
    }

But it should be in a file all by itself, which means a copyright notice, a package declaration, and, if I'm being pedantic, a Javadoc comment.

    // Copyright (c) 2008

    package com.foo.bar.baz.utility.api.helper;

    /**
     *  Abstract base class for OptionalCgiArguments.
     *  Ensures that all OptionalCgiArguments provide
     *  a marshaling method.
     *
     * @author jmarshall
     */
    public abstract class OptionalCgiArgument {
        abstract String marshal() throws MarshalingException;
    }

Did I forget the unit test?
Post a Comment