Improved radial encapsulation


A previous post presented radial encapsulation, a means of organizing Java packages to minimize package-level potential coupling and structural disorder.

That idea is almost trivial.

Oracle supplies the official term, "Subpackage," when it writes, "The naming structure for packages is hierarchical. The members of a package are class and interface types ... and subpackages."

Oracle does not define a "parent" package but genealogical terminology proves useful: thus given package structure a.b.c, we can say that b is the parent of c, a is the grand-parent of c, and that in general a and b are both antecedents of c, and b and c are descendants of a.

Radial encapsulation allows package dependencies only on antecedents.

Thus in the example above, a.b.c could not depend on a.d, because d is not a parent, grand-parent or any antecedent of c.

This constraint snubs Oracle's anaemic package mandates, by which, "... hierarchical naming structure for packages is intended to be convenient for organizing related packages in a conventional manner, but has no significance in itself." The constraint, however, heaves with it the tremendous benefit of allowing programmers to design package structures of arbitrarily low structural disorder, see figure 1 where the program on the right is radially encapsulated.

Figure 1: Two Java package structures

Figure 1: Left structural disorder 76%; right structural disorder: 3%.

To continue the example above, what if a.b.c does indeed require the services of a.d? Must we contort our package structure to squeeze d in as an antecedent of c? Of course not. We use the facade design pattern, and have d export its interfaces into a where those interfaces are then available to all.

That was classical radial encapsulation.

Steve Mcduff commented, however, "As applications grow, this solution would force ... a lot of facades in common low level packages to exist."

Steve is, unfortunately, correct. Radial encapsulation of large systems does encourage a certain ... bottom-heaviness.

So this post proposes a small improvement on radical encapsulation with the introduction of: the ground.

Take any package and declare it the "ground parent". The ground is then the set of immediate subpackages of this ground parent, on which the guiding principle of radial encapsulation is loosened: this set of subpackages that may be depended upon by any descendant of the ground parent.

Figure 2 presents a radially encapsulated structuring of the following 5 qualified packages:

  1. com.a.b
  2. com.a.c.d
  3. com.a.c.e
  4. com.a.f
  5. com.a.g.h

Figure 2: A radially encapsulated package structure

Figure 2: A radially encapsulated package structure.

In classical radial encapsulation, package e, for example, could not depend on b because b is not an antecedent of e.

With improved radial encapsulation, however, e can now depend on any of the ground packages (shown in green) and on packages a and com (both are antecedent). It cannot, however, depend on packages d or h, those packages being neither ground nor antecedent - encapsulation remains strengthened over and above Oracle's defaults.

Let's take a look at refactoring a classic radially encapsulated program into the updated variety.

Figure 3 shows the package structure of Spoiklin Soice. A typical model/view/controller split, note that the neither the model nor view nor controller packages are antecedent to one another and thus for, say, the controller to use the services of the model, then model must export its interfaces into link or spoiklin (com, edmundkirwan and base were deliberately kept sparse).

Figure 3: A radially encapsulated package structure

Figure 3: A radially encapsulated package structure.

This left spoiklin a little bloated: figure 4 shows these dependency lines (unlike figure 3, which shows hierarchy) with thickness proportional to number of dependencies.

Figure 4: Spoiklin diagram of Spoiklin Soice

Figure 4: Dependencies in the program of figure 3.

Figure 4 shows another drawback of the old approach: the diagram fails to show which interfaces in spoiklin - whether exported from model, view or controller - are most depended upon. Which part of the system suffers most from change-sensitivity because so many others depend on it?

Figure 5 presents the same system refactored to the improved radial encapsulation.

Figure 5: A newly radially encapsulated package structure

Figure 5: The new radial encapsulation.

Here, spoiklin is declared the ground parent and hence model, view or controller (and others besides) form the ground. The controller sub-tree of packages can now export its facades not into a common package but into the controller package itself, with other sub-trees similarly possessing their own dedicated ground packages. This both removes bloat and clarifies system dependencies, as shown in figure 6.

Figure 6: Spoiklin diagram of Spoiklin Soice

Figure 6: Dependencies in the newly radially encapsulated system.

Figure 6 reveals that model is the most depended upon package (and hence functionality) of the system. Yet of course, as with classical radial encapsulation, all the model sub-tree of implementation packages are hidden from all the view and controller implementations, affording the system substantial flexibility.

Summary.

Establishing a ground, that is, a set packages that may be depended upon by any descendant of the ground parent, improves radial encapsulation's scalability.

Which is good.