The quality of a design is heavily influenced by a system’s package relationships. Loosely coupled and highly cohesive packages are qualities of any well-designed application. So a good place to begin assessing design is by evaluating package relationships. Objectively measuring a system’s degree of reusability and maintainability can serve as valuable feedback in determining the quality of your software’s structure. The metrics I present below provide objective feedback that allows you to assess the quality of your designs.
While metrics can offer guidance and feedback regarding the quality of our designs, it’s important to keep in mind that the metrics I discuss should not be used as the sole mechanism upon which a design is based. It is not a certainty that good design quality metrics are indicative of good designs and that bad design quality metrics are indicative of bad designs. However, it is unlikely that you’ll find a top-notch design associated with poor design metrics. These metrics are intended to be used by designers to measure the designs they create, help in understanding those designs, and most importantly, to check that the designs exhibit the qualities expected based on the goals you’ve attempted to achieve.
Stability is measured by calculating how easy it is to change a package without impacting other packages within the application. Let’s examine how package dependencies impact stability.
Packages most heavily depended upon are the packages most heavily reused throughout an application. Packages with more incoming dependencies must, therefore, exhibit higher degrees of stability. In other words, packages most heavily depended upon are the most difficult to change. The number of incoming and outgoing dependencies is a major indicator in determining the stability or instability of a package. Packages containing many outgoing, but few incoming, dependencies are less stable, because the ramifications of change are less. On the other hand, packages containing more incoming dependencies are more stable, because they are more difficult to change. Stability can be calculated by comparing the incoming and outgoing package dependencies.
Afferent coupling represents the number of other packages that depend upon the package being measured. Efferent coupling is the number of other packages that the package being measured is dependent upon. Instability is measured by calculating the ratio of efferent coupling to total coupling. The formula used to calculate instability follows:
I = Ce/(Ce + Ca)
- I represents the degree of instability associated with a package.
- Ca represents the afferent coupling, or incoming dependencies, and
- Ce represents the efferent coupling, or outgoing dependencies.
As I approaches zero, packages have many more incoming dependencies than outgoing dependencies, and the package is very stable. Stable packages are more difficult to change because the ramifications of changing a stable package may imply additional changes to many other packages. Packages with I approaching one have many more outgoing dependencies than incoming dependencies, and the package is very instable. Instable packages are easier to change because few other packages in the application use them. At this point, you may ask, "Should I strive to create more stable or more instable packages?"
For any set of packages to compose an application, some packages must have incoming dependencies, while other packages have outgoing dependencies. Our goal in package design should not be that all packages are either completely stable or completely instable. Instead, each package must be consciously made as stable or instable as possible. Those packages with many incoming dependencies must resist change, and exhibit higher degrees of stability. In object-oriented development, abstraction can be used to increase stability by separating what something does from how it does it. In Java, abstraction is manifest in abstract classes or interfaces. So more stable packages should be more abstract packages. Conversely, more instable packages are more concrete packages.
You measure the abstractness of a package by calculating the ratio of the number of abstract classes (and interfaces) in the package to the total number of classes in the package. Abstractness can be measured using the following formula:
- A represents a package’s abstractness.
- Na represents the number of abstract classes and interfaces in a package.
- Nc represents the number of concrete classes in a package.
An abstractness value of zero indicates a completely concrete package, whereas a value of one indicates a completely abstract package.
Evaluating the metrics
Comparing abstractness and stability yields some rather interesting results. A completely instable and abstract package represents a completely abstract package with no incoming dependencies. This isn't a particularly useful scenario. Contrarily, a completely stable package with many incoming dependencies represents a package with many concrete classes that are heavily used.
This is cause for concern because a primary benefit of abstraction is to isolate change to implementations. Changing concrete classes that are heavily used can cause a ripple of changes to occur throughout the application. So you should strive to minimize a package’s incoming dependencies on concrete classes. In other words, try to design packages that are abstract in proportion to their incoming dependencies, and concrete in proportion to their outgoing dependencies.
A valuable aid for assessing design quality
These metrics should offer reinforcement that your software design is robust. While good metrics do not guarantee a quality design, good metrics do help bolster confidence. When used judiciously, these software metrics can be a valuable aid in assessing the quality of your design.
For more information
This article is based on the original work of Robert C. Martin. It is used with permission in the author's book Java Design: Objects, UML, and Process, published by Addison-Wesley. Additional information on the original work can be found at Object Mentor, Inc.