Radial encapsulation.


Radial

The basics.

In its support of packages, Java offers fertile soil for the cultivation of radial encapsulation.

Java's package structure begins with the top level package, that is, the package from which every other child package issues. Companies creating Java source code usually define their package structure by inverting their internet name, so WeatheredCheese.com will have com as its top level package and weatheredcheese as its first child.

Drawing the package structure of the code of these dairy experts would reveal something like figure 1, where the line represents a dependency between the packages (that is, the line represents the multiple dependencies between the classes of both packages).

A basic cheese structure

Figure 1: The basics of the WeatheredCheese.com package structure.

Let's say, furthermore, that WeatheredCheese decides to use the standard MVC pattern; then their package structure might reflect that presented in figure 2.

MVC WeatheredCheese

Figure 2: WeatheredCheese.com using model/view/controller.

In figure 2, com is the top level package and parent of weatheredcheese and weatheredcheese is the parent of three children of its own, the model, view and controller packages. We can also say that both com and weatheredcheese are, seen from the view package, in the direction of the top level package.

Radial encapsulation involves just one rule, a inter-package dependency restriction: Depend only on those packages in the direction of the top level package.

Depend only in the direction of the top level package.

Depending only in the direction of the top level package means that we judge all package dependencies based on the relationship between the depending packages: the only dependencies allowed are those in which the package being depended on is closer to the top level package than the package doing the depending.

Even more simply stated: diagrams which show packages being depended on below those doing the depending always show dependencies going downwards. Thus in figure 2 weatheredcheese can depend on the com package (the dependency goes down the diagram). com cannot depend on weatheredcheese, however, because weatheredcheese is not relatively in the direction of the top level com (and the dependency would go up the diagram).

model can depend on both com and weatheredcheese because both are, from model's point of view, in the direction of the top level package. model cannot depend on the view package because, again, view is not in the direction of the top level package (view is actually a sibling of model).

This single, simple dependency restriction delivers a single, substantial benefit: it reduces potential coupling.

Potential coupling - HUUHHH! - what is it good for?

Coupling is the measure of the strength of association established by a connection from one module to another or, in our case, a measure of dependencies between packages: the more dependencies that tie packages together, the more coupled they are.

Given one hundred classes evenly distributed over three packages with just one class in each package dependent on a class in another package, we safely deem our packages loosely-coupled. If all classes in the three packages are dependent on every other class in the other packages then our packages suffer from being strongly-coupled. A little extreme, perhaps, but you get the gist.

Coupling is bad business because - all else being equal - a strongly-coupled system is more expensive to change than a loosely-coupled system because changes made to a strongly-coupled system have a higher probability of necessitating other changes (the dreaded ripple effect) than those made in a loosely-coupled system.

Potential coupling is the maximum possible number of source code dependencies within a program or, in our case, the maximum possible number of dependencies between packages. Potential coupling is bad because potential coupling is the theoretical limit of actual coupling: the more potential coupling you have then more coupling you can have.

Looked at in reverse: the lower your potential coupling then the lower the maximum, strongest coupling you can have and hence the lower will be the maximum cost of ripple-effect changes.

Radial encapsulation - packages depending only in the direction of the top level package - serves its hot bowls of advantage because a radially encapsulated system will generally have a far lower potential coupling than a system whose packages depend on one another without restriction (this is called absolute encapsulation). The reasoning is obvious: if your source package can depend only on packages in the direction of the top level package then vast swathes of siblings and child-lines fall off the radar; no longer visible, they fail to appear as dependency targets and hence the maximum potential number of dependencies radically diminishes.

Observant

Practical considerations.

The observant will have spotted two minor problems.

Firstly, if, say, the controller package above cannot depend on the view package then what happens when the controller needs to access some view functionality? The answer is that the view package must export via a facade - a family of interfaces or classes - all services that other packages will consume and it must store this facade in the highest package in common with the users of those services.

In this case, if the controller needs to access some view functionality, view must export a facade to the weatheredcheese package which the controller package can legally access. Not only does this mechanism reduce potential coupling, it has a profoundly simplifying effect on the package diagram of the system. Radially-encapsulated systems boast striking visual structure.

Secondly, how would Java start a system such as that described in figure 2?

Java (ignoring certain frameworks for a moment) requires a main() method in one class which must then (indirectly) trigger the instantiation of all other objects. If we put this main() method in, say, the controller package, how will it instantiate the classes of model or view when it finds those packages decidedly off-limits under radial encapsulation?

The answer is: radial encapsulation is not possible in Java.

Not strictly, anyway.

There will always be a need to have at least one dependency into all packages regardless of their position in the package structure.

Pragmatism teaches us, however, to chin-up and just accept this. It invites us to have perhaps a single class in each package with the responsibility of instantiating the others. Yes, access to this single class violates radial encapsulation but we do not collapse in teary bitterness. We march on, applying the restriction to all those other system dependencies. Purity be damned. The prize of fabulous loose coupling is worth it.

Summary.

Restricting a system's inter-package dependencies to those proposed by radial encapsulation whereby they obey the rule, "Depend only on in the direction of the top level package," radically reduces a system's potential coupling and therefore greatly reduces the maximum ripple-effect costs that such a system might incur.

This has been a brief explanation of radial encapsulation: a more thorough description can be found here.

Photo credit attribution.

CC Image bicycle wheel courtesy of Rego Korosi on Flickr.

CC Image Microscope courtesy of Lord Biro on Flickr.