Friday, January 25, 2008
I had a friend, an experienced C++ developer who said he always felt straight-jacketed by Java packages. He didn't want to be forced to organize his files in the same way he organized his packages. I never understood why anyone would want the freedom to create the type of disaster I had seem before. But there's a part of the picture I was missing. Packages also imply something about dependencies.
Good guidelines on packaging are remarkably hard to come by. But, at minimum, a well designed package structure will minimize dependencies between packages and avoid circular dependencies. I've come to follow other rules of thumb about inter-package dependencies. Everyone can depend on a util package. But a util package should have no dependencies inside the project. Domain model classes should be defined abstractly and be free of outside dependencies.
Inversion of control is when code at a lower level of abstraction depends on code at a higher level of abstraction. The domain model is defined at a high level, with little or no implementation detail. In fact, much of my domain models are just interfaces. Lower level packages do the grunt work with specializations of the domain model artifacts.
Recently, I worked on a project with a Swing UI, which I hadn't done in years. I created a ui package to hold all the Swing related kerfuffle. I started noticing that I had a lot of actions. These little 20 line subclasses of AbstractAction define the event handlers used by controls throughout the UI and seem to multiply like rabbits. Mixed in with the rest of the UI classes, classes that formed the windows, panels, dialogs, and other on-screen widgetry of my app, the actions were hard to find and just cluttered up the place. I wanted, for strictly organization reasons, to move them out of the way.
Of course, the actions depend on the UI widgets they invoke and the UI widgets depend on the actions. So, if I put my actions in a separate package, I'd have a big ugly circular dependency staring me right in the face. I go sick of looking at the action classes, so I did it anyway. Where dependencies and name-spaces are concerned, they're all in one conceptual unit. But on disk, and therefore necessarily in the package hierarchy, they're in two different places.
Since becoming more aware of the role dependencies play in packaging, I've notice more little conflicts of interest like this. I still prefer the enforced file system structure of packages to total chaos, but I now recognize my friend's point.
It would be an interesting extension to the language to be able to explicitly declare that package X was allowed to depend on packages Y, Z, and util. Any other dependency could generate a compiler warning. There are static code analyzers that will do just that. (JDepends for example.) It would be even better if the IDE was aware of such things and could issue a warning as you added a suspicious dependency.
Wednesday, January 23, 2008
Thursday, January 17, 2008
If you're aware of two's compliment representation of integers, you can probably guess the output of the following line of Java:
Sure enough, you get this:
Knowing that, what do you think the result of this line might be?
Would you believe NumberFormatException: For input string: "ffffffff"? The reason is that decode works on signed values. Ugly with a capital Ug. Decode works as expected on hexadecimal values in which the most significant bit is clear. But here, that high bit is set. The decode function expects a negative hex value where ever you'd use a negative decimal value.
Now, what would be a proper punishment for the kind of protohuman homunculus who would use a sign with a hexadecimal number? Fifty lashes with a wet noodle for the addled mind that thought this was a good idea:
Most sane programmers find this highly annoying. There's a bug dated in 1999, (and several dupes) in the Java bug database for this, and its counterpart bug in Long. What an embarrassment.
One work-around (for integers) is to use a Long when you expect a hex number with the sign bit set.
int thisIsTotallyWeak = Long.decode("0xffffffff").intValue();