How to organize packages (and prevent dependency cycles)?

Jordi picture Jordi · Mar 22, 2009 · Viewed 21.9k times · Source

I've been running some metrics on my Java project and apparently there are a lot of dependency cycles between packages. I didn't really know how to organize stuff into packages, so I just did what made sense to me, which is apparently wrong.

My project is a neural network framework. Neural networks have Neurons, which are connected to each other with Connections. They need to depend on each other. However, there are also different types of Neurons, so I thought it'd be a good idea to put them all in there own 'neurons' package. Obviously a Connection isn't a Neuron so it shouldn't be in the package, but since they refer to each other, I now have a circular dependency.

This is just an example, but I have more situations like this. How do you handle these kinds of situations?

Also, I read that classes in a package higher up in the package hierarchy are not supposed to refer to classes in packages that are deeper. This would mean that a NeuralNetwork class in package 'nn' can not refer to the Neuron in package 'nn.neurons'. Do you guys follow this principle? And what if I would move NeuralNetwork to 'nn.networks' or something? In that case, it would refer to a sibling package instead of a child. Is that better practice?

Answer

TofuBeer picture TofuBeer · Mar 22, 2009

The antcontrib VerifyDesign task will help you do what you want:

For example, if there are three packages in one source tree

* biz.xsoftware.presentation
* biz.xsoftware.business
* biz.xsoftware.dataaccess

and naturally presentation should only depend on business package, and business should depend on dataaccess. If you define your design this way and it is violated the build will fail when the verifydesign ant task is called. For example, if I created a class in biz.xsoftware.presentation and that class depended on a class in biz.xsoftware.dataaccess, the build would fail. This ensures the design actually follows what is documented(to some degree at least). This is especially nice with automated builds

So once you have decided how things should be organized you can enforce the requirements at compile time. You also get fine-granied control so you can allow certain cases to break these "rules". So you can allow some cycles.

Depending on how you want to do things, you might find that "utils" package makes sense.

For the particular case that you cite... I might do something like this:

  • package nn contains Nueron and Connection
  • package nn.neurons contains the subclasses of Nueron

Neuron and Connection are both high-level concepts used in the NeuralNetowrk, so putting them all together makes sense. The Neuron and Connection classes can refer to each other while the Connection class has no need to know about the Neuron subclasses.