Skip to main content

4.4 Summary

high-level structure

  • 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.
  • When possible, express configuration pragmas through compiler options or other means that do not require modifications to the source code. .
  • When configuration pragmas must be placed in source code, consider isolating them to one compilation unit per partition; if specified, the main subprogram for the partition is recommended.
  • Use subprograms to enhance abstraction.
  • Restrict each subprogram to the performance of a single action.
  • Use a function when the subprogram's primary purpose is to provide a single value.
  • Minimize the side effect of a function.
  • Consider using a parameterless function when the value does not need to be static.
  • Use a parameterless function (instead of a constant) if the value should be inherited by types derived from the type.
  • Use a parameterless function if the value itself is subject to change.
  • Use packages for information hiding.
  • Use packages with tagged types and private types for abstract data types.
  • Use packages to model abstract entities appropriate to the problem domain.
  • Use packages to group together related type and object declarations (e.g., common declarations for two or more library units).
  • Encapsulate machine dependencies in packages. Place a software interface to a particular device in a package to facilitate a change to a different device.
  • Place low-level implementation decisions or interfaces in subprograms within packages.
  • Use packages and subprograms to encapsulate and hide program details that may change (Nissen and Wallis 1984).
  • If a new library unit represents a logical extension to the original abstraction, define it as a child library unit.
  • If a new library unit is independent (e.g., introduces a new abstraction that depends only in part on the existing one), then encapsulate the new abstraction in a separate library unit.
  • Use child packages to implement a subsystem.
  • Use public child units for those parts of a subsystem that should be visible to clients of the subsystem.
  • Use private child units for those parts of a subsystem that should not be visible to clients of the subsystem.
  • Use private child units for local declarations used only in implementing the package specification.
  • Use child packages to implement constructors, even when they return access values.
  • Make each package serve a single purpose.
  • Use packages to group related data, types, and subprograms.
  • Avoid collections of unrelated objects and subprograms (NASA 1987; Nissen and Wallis 1984).
  • Consider restructuring a system to move two highly related units into the same package (or package hierarchy) or to move relatively independent units into separate packages.
  • Avoid declaring variables in package specifications.
  • Use tasks to model abstract, asynchronous entities within the problem domain.
  • Use tasks to define concurrent algorithms for multiprocessor architectures.
  • Use tasks to perform concurrent, cyclic, or prioritized activities (NASA 1987).
  • Use protected types to control or synchronize access to data or devices.
  • Use protected types to implement synchronization tasks, such as a passive resource monitor.

visibility

  • Put only what is needed for the use of a package into its specification.
  • Minimize the number of declarations in package specifications.
  • Do not include extra operations simply because they are easy to build.
  • Minimize the context (with) clauses in a package specification.
  • Reconsider subprograms that seem to require large numbers of parameters.
  • Do not manipulate global data within a subprogram or package merely to limit the number of parameters.
  • Avoid unnecessary visibility; hide the implementation details of a program unit from its users.
  • Use child library units to control the visibility of parts of a subsystem interface.
  • Use private child packages for those declarations that should not be used outside the subsystem.
  • Use child library units to present different views of an entity to different clients.
  • Design (and redesign) interfaces after having worked out the logic of various expected clients of the interface.
  • Use child packages rather than nested packages to present different views of the same abstraction.
  • Nest package specifications within another package specification only for grouping operations or hiding common implementation details.
  • Consider using private child packages in lieu of nesting.
  • Restrict the visibility of program units as much as possible by nesting them inside package bodies (Nissen and Wallis 1984) if you cannot use a private child package.
  • Minimize nesting program units inside subprograms and tasks.
  • Minimize the scope within which with clauses apply.
  • Only with those units directly needed.
  • Carefully consider encapsulation of tasks.

exceptions

  • For unavoidable internal errors for which no user recovery is possible, declare a single user-visible exception. Inside the abstraction, provide a way to distinguish between the different internal errors.
  • Do not borrow an exception name from another context.
  • Export (declare visibly to the user) the names of all exceptions that can be raised.
  • In a package, document which exceptions can be raised by each subprogram and task entry.
  • Do not raise exceptions for internal errors that can be avoided or corrected within the unit.
  • Do not raise the same exception to report different kinds of errors that are distinguishable by the user of the unit.
  • Provide interrogative functions that allow the user of a unit to avoid causing exceptions to be raised.
  • When possible, avoid changing state information in a unit before raising an exception.
  • Catch and convert or handle all predefined and compiler-defined exceptions at the earliest opportunity.
  • Do not explicitly raise predefined or implementation-defined exceptions.
  • Never let an exception propagate beyond its scope.
note

This page of the "Ada Quality and Style Guide" has been adapted from the original work at https://en.wikibooks.org/wiki/Ada_Style_Guide, which is licensed under the Creative Commons Attribution-ShareAlike License; additional terms may apply. Page not endorsed by Wikibooks or the Ada Style Guide Wikibook authors. This page is licensed under the same license as the original work.