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:
- 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.
- 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:
- 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.
- 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.