Java packages are often used like file-system folders to organize source. But source files differ from “normal” files in that they are highly inter-dependent. Considering this interdependence as a package hierarchy evolves can have significant productivity benefits.
Packages as Folders
Java packages provide an ideal way to organize code into a scalable, hierarchical structure that helps us find specific code.
In this sense, packages can be used like folders in a file-system:
- We place files with something in common in the same folder.
- When a folder grows too big and we find we’re having trouble finding files, we split the folder into sub-folders according to some criteria that makes sense to us.
- We share files by placing them in a common area on company network, in which case the structure evolves according to the varying criteria of different people.
- We often have trouble deciding which folder a file best belongs, and make an arbitrary decision.
Often Packages are only used as a kind of filesystem equivallent. However the package hierarchy can also be used to reinforce the intended design and associated development activities.
Packages as Design Abstractions
Source files differ from other files typically stored in the file-system:
- They depend on the detailed contents of other source files.
- Are created and edited in groups of multiple files.
- Are subject to a high number of relatively small changes.
- Are edited by a team of developers rather than individuals.
- Should be reusable on future projects.
- Should be easy to change without impacting other files too widely.
- Must support the defined deployment environment.
- Are subject to a QA, version control and release processes.
The aim of a package design should be to support these characteristics. For example, the design could explicitly support Martin’s “Reuse/Release Equivalence Principle (REP)” (article, book) whereby packages are developed, built, tested and released against released versions of the packages upon which they depend.
Design is not something that happens once at the start of the project – it is an activity that spans the life of an application or product. This fact has become explicit with the iterative and Agile development models. As the code-level design continues, the package-level design emerges.
Unfortunately, the emergent design is often invisible and so forgotten. Not only does the original design degrade, but the overall structure tends to become excessively complex. As the supporting structure dissolves, development activities become harder and the cost of each new feature increases.
This priciple of emergent design is important here. Clearly when I have a project of 50 classes in a half-dozen packages, the overhead of a sub-optimal package dependencies isn’t going to slaughter me. But if my project is going to grow to 5000 classes, then putting in minimal effort from the start can save huge effort when things get more complicated.
In part 2 I’ll take a closer look at cyclic package dependencies and why they matter.