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.