Package-Level Principles

All of the Package-Level Principles listed by Knoernschild are either trivial or apply equally well when we substitute class, object, function, or file for package.

By definition, a package (or module) p1 depends on another package (or module) p2 if some change to p2 forces a change to p1. In UML notation we write:

How is this possible? This can only happen if some member of p1 depends on some member of p2. For example, class p1.A depends on class p2.B.

Knoernschild points out that p2.B probably depends on other classes in p2, and so p1.A may have indirect dependencies on these other classes. This is just the transitivity property of the depends relationship. It doesn't matter if the class p2.B depends on is in p2 or not.

Of course the Modularity Principle tells us that modules should be cohesive and loosely coupled. This applies when modules are taken to be packages. The members of a package should be closely related in purpose.

There are several structuring techniques that can be used to improve the design of a package. The Facade pattern can lower the coupling degree between modules, and the Mediator pattern can reduce the dependencies between the members of a package.

In some languages package members can have public or package scope. A member with public scope is said to be exported by the package. A member with package scope usually supports public members.

In some languages packages can have subpackages. The members of a subpackage can be considered to be members of the super package (transparent packages) or not (opaque packages, like in Java.)

We can also define the notion of stability and abstractness for packages.

Acyclic Dependency Principle

Avoid cycles in the package-level dependency graph

It is sometimes difficult to avoid cycles in the class-level dependency graph. Cycles in the package-level dependency graph may be unavoidable, too, but this is often a sign that the Modularity Principle is being violated.

For example, assume there is a bi-directional dependency between p1 and p2:

Often we can factor those members of p2 that depend on p1 into a separate package p2', thus eliminating the cycle: