Previous | Table of Contents | Next |
10.4.2.1. A Root Package
First we introduce our structure and naming convention. By analogy with the standard library structure in which all standard packages are descendants (children or grandchildren) of a root package Ada, in this chapter all the packages are descendants of a root package HB:
1 package HB is 2 -- Root package for Handbook examples 3 end HB;
This package interface provides no resources; it exists purely to serve as the root. Because this interface is empty, there is no need to provide an implementation.
Ada does not require you to use a root package. I choose to do so in conformity with a growing trend in the Ada literature, in which the provider of a given collection of packages uses a root package to provide a distinctive top-level name for all packages in the collection. In this way, the user of different collections can tell the origin of a given package; further, the likelihood of duplicated package names is minimized.
10.4.2.2. A Package for Rational-Number Arithmetic
The first package example is HB.Rationals, which provides a type and a set of programmer-defined operations on rational numbers (fractions). The Ada standard provides for integer, fixed-point, and floating-point scalar arithmetic, and Annex G, the standard numerics annex, provides for complex numbers and arithmetic. On the other hand, Ada provides no predefined support for rationals, which have many applications.
A rational value has two components: the numerator and the denominator. Here is the package interface for HB.Rationals.
1 package HB.Rationals is 2 3 type Rational is private; 4 5 ZeroDenominator: exception; 6 7 function / (X, Y: Integer) return Rational; 8 -- constructor: 9 -- Pre : X and Y are defined 10 -- Post: returns a rational number 11 -- If Y > 0, returns X/Y 12 -- If Y < 0, returns (-X)/(-Y) 13 -- Raises: ZeroDenominator if Y = 0 14 15 function Numer (R : Rational) return Integer; 16 function Denom (R : Rational) return Positive; 17 -- selectors: 18 -- Pre: R is defined 19 -- Post: return numerator and denominator respectively 20 21 function = (R1, R2 : Rational) return Boolean; 22 function < (R1, R2 : Rational) return Boolean; 23 function <=(R1, R2 : Rational) return Boolean; 24 function > (R1, R2 : Rational) return Boolean; 25 function >=(R1, R2 : Rational) return Boolean; 26 -- comparators: 27 -- Pre : R1 and R2 are defined 28 -- Post: return the obvious Boolean results 29 30 function +(R: Rational) return Rational; 31 function -(R: Rational) return Rational; 32 function ABS(R: Rational) return Rational; 33 function +(R1, R2 : Rational) return Rational; 34 function -(R1, R2 : Rational) return Rational; 35 function *(R1, R2 : Rational) return Rational; 36 function /(R1, R2 : Rational) return Rational; 37 -- Pre : R, R1 and R2 are defined 38 -- Post: return the obvious arithmetic results 39 40 private 41 42 type Rational is record 43 Numerator : Integer := 0; 44 Denominator: Positive := 1; 45 end record; 46 47 end HB.Rationals;
Line 3 gives a partial view of a type Rational: The type is declared as private; its structure is not provided here. I make the type private for encapsulation purposes: The structural details of a private type are not visible to client programs, and the only operations predefined on private types are assignment and equality/inequality tests.
Using a private type gives me the flexibility to chooseand later changethe internal structure without requiring any changes to client programs. Further, I can provide a complete set of operations on the type with no worry that a client will be able to use any additional operations. This gives me control over the abstraction provided by the package.
Ada also provides for limited private types, which have no predefined operations at all, not even assignment and equality test. That is, the programmer of a package providing a limited private type is assured that a client program can do nothing with objects of the type except those operations explicitly provided in the package. This is useful in situations where, for example, it is necessary or desirable to prohibit clients from copying one object to another. The type Ada.Text_IO.File_Type is a predefined limited private type.
The full view of the private type Rational is given in the private part of the interface (lines 40-46). You see that a rational is represented as a record, that its numerator can be an arbitrary integer but that its denominator is positive, and that each rational quantity is initialized. Human readers of the package interface can obviously see this full view, but client programs cannot. A client program therefore cannot refer directly to the numerator and denominator fields but must use package-provided operations to do so.
Line 5 defines a package-specific exception ZeroDenominator; you shall see later how this is raised if a client tries to assign 0 to the denominator of a rational.
The declaration (line 7)
function / (X, Y: Integer) return Rational;
declares an operator /, which clients can use to construct a rational. This operator allows you to write, in client programs, statements such as
R: Rational := 1/3;
Previous | Table of Contents | Next |