Ada 95 Quality and Style Guide Chapter 9

Chapter 9: Object-Oriented Features - TOC - 9.2 TAGGED TYPE HIERARCHIES

9.2.1 Tagged Types

guideline

  • Consider using type extension when designing an is-a (generalization/specialization) hierarchy.
  • Use tagged types to preserve a common interface across differing implementations (Taft 1995a).
  • When defining a tagged type in a package, consider including a definition of a general access type to the corresponding class-wide type.
  • In general, define only one tagged type per package.

  • example

    Consider the type structure for a set of two-dimensional geometric objects positioned in a Cartesian coordinate system (Barnes 1996). The ancestor or root type Object is a tagged record. The components common to this type and all its descendants are an x and y coordinate. Various descendant types include points, circles, and arbitrary shapes. Except for points, these descendant types extend the root type with additional components; for example, the circle adds a radius component:

    type Object is tagged
       record
          X_Coord : Float;
          Y_Coord : Float;
       end record;
    
    type Circle is new Object with
       record
          Radius : Float;
       end record;
    
    type Point is new Object with null record;
    
    type Shape is new Object with
       record
          -- other components
          ...
       end record;
    

    The following is an example of general access type to the corresponding class-wide type:

    package Employee is
       type Object is tagged limited private;
       type Reference is access all Object'class;
       ...
    private
       ...
    end Employee;
    
    

    rationale

    You can derive new types from both tagged and untagged types, but the effects of this derivation are different. When you derive from an untagged type, you are creating a new type whose implementation is identical to the parent. Values of the derived types are subject to strong type checking; thus, you cannot mix the proverbial apples and oranges. When you derive a new type from an untagged type, you are not allowed to extend it with new components. You are effectively creating a new interface without changing the underlying implementation (Taft 1995a).

    In deriving from a tagged type, you can extend the type with new components. Each descendant can extend a common interface (the parent's). The union of a tagged type and its descendants form a class, and a class offers some unique features not available to untagged derivations. You can write class-wide operations that can be applied to any object that is a member of the class. You can also provide new implementations for the descendants of tagged types, either by overriding inherited primitive operations or by creating new primitive operations. Finally, tagged types can be used as the basis for multiple inheritance building blocks (see Guideline 9.5.1).

    Reference semantics are very commonly used in object-oriented programming. In particular, heterogeneous polymorphic data structures based on tagged types require the use of access types. It is convenient to have a common definition for such a type provided to any client of the package defining the tagged type. A heterogeneous polymorphic data structure is a composite data structure (such as an array) whose elements have a homogeneous interface (i.e., an access to class-wide type) and whose elements' implementations are heterogeneous (i.e., the implementation of the elements uses different specific types). See also Guidelines 9.3.5 on polymorphism and 9.4.1 on managing visibility of tagged type hierarchies.

    In Ada, the primitive operations of a type are implicitly associated with the type through scoping rules. The definition of a tagged type and a set of operations corresponds together to the "traditional" object-oriented programming concept of a "class." Putting these into a package provides a clean encapsulation mechanism.

    exceptions

    If the root of the hierarchy does not define a complete set of values and operations, then use an abstract tagged type (see Guideline 9.2.4). This abstract type can be thought of as the least common denominator of the class, essentially a conceptual and incomplete type.

    If a descendant needs to remove one of the components or primitive operations of its ancestor, it may not be appropriate to extend the tagged type.

    An exception to using reference semantics is when a type is exported that would not be used in a data structure or made part of a collection.

    If the implementation of two tagged types requires mutual visibility and the two types are generally used together, then it may be best to define them together in one package, though thought should be given to using child packages instead (see Guideline 9.4.1). Also, it can be convenient to define a small hierarchy of (completely) abstract types (or a small part of a larger hierarchy) all in one package specification; however, the negative impact on maintainability may outweigh the convenience. You do not provide a package body in this situation unless you have declared nonabstract operations on members of the hierarchy.


    < 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