Ada 95 Quality and Style Guide Chapter 4

Chapter 4: Program Structure - TOC - 4.1 HIGH-LEVEL STRUCTURE

4.1.1 Separate Compilation Capabilities

guideline

  • Place the specification of each library unit package in a separate file from its body.
  • Avoid defining library unit subprograms that are not intended to be used as main programs. If such subprograms are defined, then create an explicit specification, in a separate file, for each library unit subprogram.
  • Minimize the use of subunits.
  • In preference to subunits, use child library units to structure a subsystem into manageable units.
  • Place each subunit in a separate file.
  • Use a consistent file naming convention.
  • In preference to nesting in a package body, use a private child and with it to the parent body.
  • Use private child unit specifications for data and subprograms that are required by (other) child units that extend a parent unit's abstraction or services.

  • example

    The file names below illustrate one possible file organization and associated consistent naming convention. The library unit name uses the adb suffix for the body. The suffix ads indicates the specification, and any files containing subunits use names constructed by separating the body name from the subunit name with an underscore:

    text_io.ads                 -- the specification
    text_io.adb                 -- the body
    text_io_integer_io.adb      -- a subunit
    text_io_fixed_io.adb        -- a subunit
    text_io_float_io.adb        -- a subunit
    text_io_enumeration_io.adb  -- a subunit
    

    Depending on what characters your file system allows you to use in file names, you could show the distinction between parent and subunit name more clearly in the file name. If your file system allows the "#" character, for example, you could separate the body name from the subunit name with a #:

    text_io.ads                 -- the specification
    text_io.adb                 -- the body
    text_io#integer_io.adb      -- a subunit
    text_io#fixed_io.adb        -- a subunit
    text_io#float_io.adb        -- a subunit
    text_io#enumeration_io.adb  -- a subunit
    

    Some operating systems are case sensitive, although Ada itself is not a case-sensitive language. For example, you could choose a convention of all lowercase file names.

    rationale

    The main reason for the emphasis on separate files in this guideline is to minimize the amount of recompilation required after each change. Typically, during software development, bodies of units are updated far more often than specifications. If the body and specification reside in the same file, then the specification will be compiled each time the body is compiled, even though the specification has not changed. Because the specification defines the interface between the unit and all of its users, this recompilation of the specification typically makes recompilation of all users necessary in order to verify compliance with the specification. If the specifications and bodies of the users also reside together, then any users of these units will also have to be recompiled and so on. The ripple effect can force a huge number of compilations that could have been avoided, severely slowing the development and test phase of a project. This is why you should place specifications of all library units (nonnested units) in separate files from their bodies.

    Library unit subprograms should be minimized. The only real use for library unit subprograms is as the main subprogram. In almost all other cases, it is better to embed the subprogram into a package. This provides a place (the package body) to localize data needed by the subprogram. Moreover, it cuts down on the number of separate modules in the system.

    In general, you should use a separate specification for any library subprogram that is mentioned in a with clause. This makes the with'ing unit dependent on the library subprogram specification, not its body.

    You should minimize the use of subunits because they create maintenance problems. Declarations appearing in the parent body are visible in the subunit, increasing the amount of data global to the subunit and, thus, increasing the potential ripple effect of changes. Subunits hinder reuse because they provide an incentive to put otherwise reusable code in the subunit directly rather than in a common routine called from multiple subprograms.

    With the availability of child library units in Ada 95, you can avoid most uses of subunits. For example, instead of using a subunit for a large nested body, you should try to encapsulate this code in a child library unit and add the necessary context clauses. You can modify the body of the child unit without having to recompile any of the other units in a subsystem.

    An additional benefit of using multiple, separate files is that it allows different implementors to modify different parts of the system at the same time with conventional editors, which do not allow multiple concurrent updates to a single file.

    Finally, keeping bodies and specifications separate makes it possible to have multiple bodies for the same specification or multiple specifications for the same body. Although Ada requires that there be exactly one specification per body in a system at any given time, it can still be useful to maintain multiple bodies or multiple specifications for use in different builds of a system. For example, a single specification may have multiple bodies, each of which implements the same functionality with a different tradeoff of time versus space efficiency, or, for machine-dependent code, there may be one body for each target machine. Maintaining multiple package specifications can also be useful during development and test. You may develop one specification for delivery to your customer and another for unit testing. The first one would export only those subprograms intended to be called from outside of the package during normal operation of the system. The second one would export all subprograms of the package so that each of them could be independently tested.

    A consistent file naming convention is recommended to make it easier to manage the large number of files that may result from following this guideline.

    In implementing the abstraction defined in a package specification, you often need to write supporting subprograms that manipulate the internal representation of the data. These subprograms should not be exported on the interface. You have a choice of whether to place them in the package body of the parent program or in a child package named in a context clause of the parent package body. When you place them in the parent package body, you make them inaccessible to all clients of the parent, including extensions of the parent declared in child packages. If these subprograms are needed to implement extensions of the parent abstraction, you would be forced to modify both the parent specification and the body because you would have to declare the extensions within the parent specification. This technique would then force recompilation of the entire package (specification and body) as well as all its clients.

    Alternatively, you can implement the supporting subprograms in a private child package. Because the parent unit's specification is not modified, neither it nor its clients need to be recompiled. The data and subprograms that might have declared in the parent unit body must now be declared in the private child unit's specification to make them visible to both the parent unit body and to any child units that extend the parent unit's services or abstractions. (See also Guidelines 4.1.6 and 4.2.) This use of private child units will generally minimize recompilations within the unit family and among its clients.

    In declaring the child package private, you achieve a similar effect to declaring it in the parent package body to the extent that clients of the parent cannot name the private child in a context clause. You gain flexibility because now you can extend the parent abstraction using child packages without having to recompile the parent specification or its body, assuming that you do not otherwise modify the parent or its body. This added flexibility will usually compensate for the increased dependency between units, in this case, the additional context clause on the parent body (and other child package bodies) that names the private child package of supporting subprograms.


    < Previous Page Search Contents Index Next Page >
    1 2 3 4 5 6 7 8 9 10 11
    TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC
    Appendix References Bibliography