The Problem

Many respected software engineers (e.g., [Parnas 72]) have long argued that potentially significant quality and productivity gains can be achieved by faithful use of abstraction, encapsulation, and layering (A/E/L). In this approach, higher-level parts of the system are layered on top of lower-level encapsulated abstractions. The claimed benefits stem largely from separation of concerns between a component's implementer and a component's client. The component implementer needs to understand only the abstract interface, not its use by the client; the client can reason abstractly about higher layers of software knowing only the abstract interface, not its implementation. If underlying representation or algorithmic details change (e.g., to improve performance) the higher layers remain stable. This modularity property is especially important when the lower-level abstractions are reusable software components [Ernst 91,Weide 91].

In our experience as ``used-program clients'' (apologies to [Tracz 88]), we have noticed that A/E/L principles are not faithfully observed by some used-program salesmen. There are two main problems:

  1. Some component libraries do not use A/E/L principles as much as they could, within those libraries. For example, [Booch 87] represents a ``map'' abstraction as a hash table using chaining for collision resolution. But he codes from scratch the lists that implement chains. He does not reuse the list package.

  2. The designs of components sometimes interact with each other and with certain language features to make it difficult for clients to respect abstraction while layering new functionality on top of existing components. The Booch components again provide an example. Ada's restriction on the mode for parameters to functions, mixed use of private and limited private types, and a variety of details of the component designs combine to make it surprisingly difficult for clients of these components (and all others we know of) to observe A/E/L principles [Hollingsworth 91]. It is, therefore, usually considered fortunate (although it probably should not be [Muralidharan 90b]) that many component libraries are in source form. This makes it possible for clients to extend and change component interfaces to suit their own needs—not by layering on top of encapsulated abstractions but by directly modifying them.

Why should such an apparently well-established principle of software engineering—especially one that seems to form the foundation for software reuse—still be so elusive in practice? We suggest there are two main reasons:

  1. There are some obvious disadvantages to A/E/L that temper the claimed advantages. The most important is that performance suffers. Secondary operations implemented by layering involve extra procedure call overhead. This is usually a small constant-factor performance penalty that can be reduced with aggressive inlining and other compiler optimizations; yet it may be important in some applications. But secondary operations also may be slow because the primary operations provided by an underlying component do not offer the proper abstract functionality, with the right performance, to permit a layered implementation to execute as quickly as if it were permitted to access underlying representations. This can be an order-of-magnitude performance penalty that the client cannot overcome except by violating abstraction—prying open an encapsulated component and delving into the guts of its implementation.

  2. While A/E/L may have many software engineering benefits in principle, apparently there are no controlled empirical studies that document measurable quality or productivity benefits of using these techniques. In making a trade-off between the analyzable and measurable performance penalties associated with faithful adherence to A/E/L, and the largely hypothetical and unquantified quality and productivity gains, a designer or manager is clearly tempted to opt for performance. This is particularly true where the programming language contains ``features'' such as code inheritance that seem to support layering, but that actually encourage violation of abstraction and encapsulation [LaLonde 89,Muralidharan 90a,Raj 90].

We are impressed by the importance of the second point in many informal discussions with software practitioners. Some claim to see the benefits of remaining completely faithful to A/E/L. They blame short-term thinking by management, inflexible deadlines, unrealistic performance objectives, and a variety of other factors, for violations of principles. The true skeptics, though, harbor sincere doubts about the claimed advantages of A/E/L in practice. They really would like to see some empirical evidence that a vigilant adherence to A/E/L actually ``works.''

Having contributed already to the hypothetical academic arguments for essentially complete allegiance to A/E/L principles in design of reusable software components [Harms 91,Weide 91], we considered how we might influence potential industrial collaborators to undertake a realistic empirical evaluation of the benefits of this approach. During summer 1991 we used a class of 18 graduate and upper- division undergraduate students to conduct an empirical pilot study of some productivity and quality effects of A/E/L in the context of reusable software components. The main purpose of this paper is to present preliminary results of that study, which support our position that observing A/E/L principles is an important factor in obtaining the claimed productivity and quality benefits of reuse.