Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to organize packages (and prevent dependency cycles)?

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?

like image 652
Jordi Avatar asked Mar 22 '09 12:03

Jordi


People also ask

How can I stop cycle dependencies?

To reduce or eliminate circular dependencies, architects must implement loose component coupling and isolate failures. One approach is to use abstraction to break the dependency chain. To do this, you introduce an abstracted service interface that delivers underlying functionality without direct component coupling.

What are dependency packages?

A dependency is another package that your package needs in order to work. Dependencies are specified in your pubspec. You list only immediate dependencies—the software that your package uses directly.

How do you determine the dependency of a cycle?

Analyze cyclic dependenciesFrom the main menu, select Code | Analyze Code | Cyclic Dependencies. In the Specify Cyclic Dependency Analysis Scope dialog, select the scope of files that you want to analyze. Select the Include test sources option if you want to analyze your test code together with the production code.


2 Answers

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.

like image 145
TofuBeer Avatar answered Oct 02 '22 13:10

TofuBeer


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.

like image 45
Dan Avatar answered Oct 02 '22 14:10

Dan