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.

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.
Condensed version: There are at least two purposes for which you might try and use packages. One is to organize code. The other is as a boundary for dependencies. Those two purposes don't always point in the same direction.
ReplyDelete