How to organize packages (and prevent dependency cycles)?
First of all, you are rightfully concerned because circular dependencies between packages are bad. Problems that come out of it grow in importance with the size of the project, but no reason to tackle this situation on time.
You should organize your classes by placing classes that you reuse together in the same package. So, if you have for example AbstractNeuron and AbstractConnection, you’d place them in the same package. If you now have implementations HumanNeuron and HumanConnection, you’d place these in the same package (called for example *.network.human). Or, you might have only one type of connection, for example BaseConnection and many different Neurons. The principle stays the same. You place BaseConnection together with BaseNeuron. HumanNeuron in its own package together with HumanSignal etc. VirtualNeuron together with VirtualSignal etc. You say: “Obviously a Connection isn't a Neuron so it shouldn't be in the package..”. This is not that obvious, nor correct to be exact.
You say you placed all your neurons in the same package. Neither this is correct, unless you reuse all your implementations together. Again, take a look at scheme I described above. Either your project is so small you place all in the single package, or you start organizing packages as described. For more details take a look at The Common Reuse Principle:
THE CLASSES IN A PACKAGE ARE REUSED TOGETHER. IF YOU REUSE ONE OF THE CLASSES IN A PACKAGE, YOU REUSE THEM ALL.
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.