Ada 95 Quality and Style Guide Chapter 8
Reusability is the extent to which code can be used in different applications with minimal change. As code is reused in a new application, that new application partially inherits the attributes of that code. If the code is maintainable, the application is more maintainable. If it is portable, then the application is more portable. So this chapter's guidelines are most useful when all of the other guidelines in this book are also applied.
Several guidelines are directed at the issue of maintainability. Maintainable code is easy to change to meet new or changing requirements. Maintainability plays a special role in reuse. When attempts are made to reuse code, it is often necessary to change it to suit the new application. If the code cannot be changed easily, it is less likely to be reused.
There are many issues involved in software reuse: whether to reuse parts, how to store and retrieve reusable parts in a library, how to certify parts, how to maximize the economic value of reuse, how to provide incentives to engineers and entire companies to reuse parts rather than reinvent them, and so on. This chapter ignores these managerial, economic, and logistic issues to focus on the single technical issue of how to write software parts in Ada to increase reuse potential. The other issues are just as important but are outside of the scope of this book.
One of the design goals of Ada was to facilitate the creation
and use of reusable parts to improve productivity. To this end,
Ada provides features to develop reusable parts and to adapt them
once they are available. Packages, visibility control, and separate
compilation support modularity and information hiding (see guidelines
in
Sections 4.1, 4.2, 5.3, and 5.7). This allows the separation of
application-specific parts of the code, maximizes the general
purpose parts suitable for reuse, and allows the isolation of
design decisions within modules, facilitating change. The Ada
type system supports localization of data definitions so that
consistent changes are easy to make. The Ada inheritance features
support type extension so that data definitions and interfaces
may be customized for an application. Generic units directly support
the development of general purpose, adaptable code that can be
instantiated to perform specific functions. The Ada 95 improvements
for object-oriented techniques and abstraction support all of
the above goals. Using these features carefully and in conformance
to the guidelines in this book, produces code that is more likely
to be reusable.
Reusable code is developed in many ways. Code may be scavenged from a previous project. A reusable library of code may be developed from scratch for a particularly well-understood domain, such as a math library. Reusable code may be developed as an intentional byproduct of a specific application. Reusable code may be developed a certain way because a design method requires it. These guidelines are intended to apply in all of these situations.
The experienced programmer recognizes that software reuse is much more a requirements and design issue than a coding issue. The guidelines in this section are intended to work within an overall method for developing reusable code. This section will not deal with artifacts of design, testing, etc. Some research into reuse issues related specifically to the Ada language can be found in AIRMICS (1990), Edwards (1990), and Wheeler (1992).
Regardless of development method, experience indicates that reusable code has certain characteristics, and this chapter makes the following assumptions:
This chapter should not be read in isolation. In many respects, a well-written, reusable component is simply an extreme example of a well-written component. All of the guidelines in the previous chapters and in Chapter 9 apply to reusable components as well as components specific to a single application. As experience increases with the 1995 revision to the Ada standard, new guidelines may emerge while others may change. The guidelines listed here apply specifically to reusable components.
Guidelines in this chapter are frequently worded "consider
. . ." because hard and fast rules cannot apply in all situations.
The specific choice you can make in a given situation involves
design tradeoffs. The rationale for these guidelines is intended
to give you insight into some of these tradeoffs.
It is particularly important that parts intended for reuse should
be easy to understand. What the part does, how to use it, what
anticipated changes might be made to it in the future, and how
it works are facts that must be immediately apparent from inspection
of the comments and the code itself. For maximum readability of
reusable parts, follow the guidelines in Chapter 3, some of which
are repeated more strongly below.
8.1.1 Application-Independent Naming
guideline
guideline
rationale
guideline
rationale
The following guidelines improve the robustness of Ada code. It
is easy to write code that depends on an assumption that you do
not realize that you are making. When such a part is reused in
a different environment, it can break unexpectedly. The guidelines
in this section show some ways in which Ada code can be made to
automatically conform to its environment and some ways in which
it can be made to check for violations of assumptions. Finally,
some guidelines are given to warn you about errors that Ada does
not catch as soon as you might like.
8.2.1 Named Numbers
guideline
rationale
guideline
rationale
guideline
guideline
guideline
rationale
guideline
guideline
rationale
Reusable parts often need to be changed before they can be used
in a specific application. They should be structured so that change
is easy and as localized as possible. One way of achieving adaptability
is to create general parts with complete functionality, only a
subset of which might be needed in a given application. Another
way to achieve adaptability is to use Ada's generic construct
to produce parts that can be appropriately instantiated with different
parameters. Both of these approaches avoid the error-prone process
of adapting a part by changing its code but have limitations and
can carry some overhead.
Anticipated changes, that
is, changes that can be reasonably foreseen by the developer of
the part, should be provided for as far as possible. Unanticipated
changes can only be accommodated by carefully structuring a part
to be adaptable. Many of the considerations pertaining to maintainability
apply. If the code is of high quality, clear, and conforms to
well-established design principles such as information
hiding, it is easier to adapt in unforeseen ways.
8.3.1 Complete Functionality
guideline
rationale
guideline
guideline
guideline
8.1 UNDERSTANDING AND CLARITY
example
rationale
8.1.2 Abbreviations
example
example
8.2 ROBUSTNESS
example
example
example
rationale
notes
8.2.4 Subtypes in Generic Specifications
example
rationale
notes
8.2.5 Overloading in Generic Units
example
rationale
8.2.7 Exceptions
example
8.3 ADAPTABILITY
example
rationale
8.3.3 Formal Private and Limited Private Types
example
rationale
notes
8.3.4 Using Generic Units to Encapsulate Algorithms
example
rationale
Chapter 8 continued on next page.
In This Guide:
Table of Contents
Chapter 1
Chapter 2
Chapter 3
Chapter 4
Chapter 5
Chapter 6
Chapter 7
Chapter 8
Chapter 9
Chapter 10
Chapter 11
Appendix
References
Bibliography
Index