Section 24.1. DEPENDENCY MANAGEMENT PRINCIPLES


24.1. DEPENDENCY MANAGEMENT PRINCIPLES

Nearly all practicing software developers have felt the painful effects of brittle softwaresoftware in which seemingly minor changes in one area break functionality in far distant areas. Like brittle glass that shatters catastrophically on a minor impact, brittle software systems fail in widespread and unpredictable ways on minor changes in one small subsystem. The reason is often poorly engineered dependencies, in which every subsystem depends upon every other, and changes to one have the potential to affect them all. Dependency management is the design discipline of removing or avoiding restrictive, failure-propagating dependencies that shorten the maintainable life of a system and increase its cost over time. Generally, dependency management involves extending the materials metaphor, converting dependencies to a more malleable or flexible form. The goal of software dependency management is to avoid oxymoronic "brittle/soft"-ware.

24.1.1. Dependencies

Fair comparisons between object-oriented programming (OOP) and aspect-oriented programming (AOP) technologies require a clear definition of dependency. Two broad definitions are available:

  1. The compiler's perspective: Which other modules require recompilation after a developer changes one of them?

  2. The developer's perspective: Which other modules need to be edited (or might have new bugs) after editing one of them?

In traditional languages, the two perspectives are equivalent. For example, editing a C++ header file generally requires corollary editing and recompiling of some or all files that include that header. The equivalence disappears with the quantification (nonlinear mapping from source to binary) that is characteristic of AOSD [5]. From the compiler's perspective, aspect weaving or subject composition languages fare poorly against OO alternatives precisely because of their automated weaving or composition.

This is the era of simpler languages compiled to byte code by GHz workstations, so we focus on the developer's perspective. A dependency, for our purposes, is a pathway for the propagation of software changes. If a subsystem is changed, those that "depend" on it may also need to be changed. Poorly managed dependencies result in excessive maintenance efforts that spread across subsystems like cracks in a windshield. Well-managed dependencies are characteristic of systems that have been prepared to meet their future changes easily and reliably.

24.1.2. Dependency Rigidity

Dependencies have varying levels of "rigidity." For example, Riel [17] identifies several levels of coupling between classes. Primary among them are nil, export (interface), and overt (implementation) coupling. With AOSD, it is possible to refine the list. Considered in relation to a class or another aspect, an aspect may be:

  1. Independent (no dependency)

  2. Interface-dependent

  3. Interface-modifying

  4. Implementation-dependent

  5. Implementation-modifying

An interface-modifying aspect is one that makes a new or changed interface available to clients but does not make use of existing implementation details. An implementation-dependent aspect makes use of original implementation details in order to augment them (for example, causing a side effect whenever a private method is called). An implementation-modifying aspect changes the behavior of the original class (for example, restricting changes to a member variable to leave it between 0 and 100).

24.1.3. Principles

The first principle for dependency management is trivial: avoid them. The second principle is nearly as simple: Keep the rigidity of each dependency as low as possible. Things become more interesting when we consider the direction of a dependency. Several principles for managing dependencies in object-oriented systems have been well articulated by Robert Martin [12]. Three are key, and all deal with dependency direction:

  • Acyclic Dependencies Principle (ADP): Dependencies must not form cycles.

  • Dependency Inversion Principle (DIP): Depend upon abstractions; do not depend upon concretions.

  • Stable Dependencies Principle (SDP): Depend in the direction of stability.

Figure 24-1 summarizes these principles by placing classes on a coordinate system of abstraction versus stability. Well-designed classes or packages in this coordinate system fall on a diagonal from lower left to upper right. Dependencies should be directed upward (DIP) and rightward (SDP).

Figure 24-1. Plotting a design on a coordinate system of abstractness versus instability can reveal how well dependencies have been managed in that design.


Some definitions are in order. Abstract and concrete are terms familiar to object-oriented practitioners, but add to their connotation of unimplemented methods the more qualitative concept of encapsulationhow well an abstraction captures one and only one idea. The other dimension, stability, is a counterintuitive one. In most disciplines, stability is a good thing. Even in software development, it's a good thing for library modules, but from the perspective of a development team working on its own code, stability can be a bad thing. Part of the great economic value of software is that it is "soft"it can be changed rapidly. Stability and rapid change are nearly opposites. In fact, as we see at the bottom of Figure 24-1, the penalty for change can be an equivalent and more intuitive way to represent software "stability."

Phrased in terms of change penalty, in concert with the definition of dependency as the change propagation path, the SDP becomes obvious: subsystems that are difficult to change should not depend upon those that are easy to change (and therefore do change frequently). The trick is to distribute a system over the change penalty continuum so that "likely to change" aligns with "easy to change" thus reaping the flexibility benefits that software brings over time. The abstraction-change penalty diagram and the principles behind it are the key tools to explore object-oriented and aspect-oriented techniques for realizing these benefits.

One intuitive difficulty with abstraction-change penalty diagrams is that "good" dependencies are upward in direction, whereas most software design diagrams show downward dependencies. The reason is the even stronger diagramming convention of placing abstract classes above concrete classes, as in an inheritance diagram. This conflict with a designer's intuition is a reminder that inheritance associations are also dependencies. It also illustrates some of the problems of inheritance, such as the "fragile base class problem," where apparently innocuous changes to a base class too readily break derived classes or their clients.



Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net