Previous | Table of Contents | Next |
The new type has a total of three operations, each in a different category:
procedure Set_Name(I: in out Instrument; S: in String);
Similarly, the type Gauge, another extension of Instrument, is declared (lines 12-18) with one overriding operation and one inherited one. Now Graphic_Gauge is declared (lines 20-24) as an extension of Gauge. I now have a three-level hierarchy of types, namely Instrument, Gauge, and Graphic_Gauge.
HB.Instruments.Clocks introduces two more instrument types.
1 package HB.Instruments.Clocks is 2 3 subtype Sixty is Integer range 0 .. 60; 4 subtype Twenty_Four is Integer range 0 .. 24; 5 6 type Clock is new Instrument with record 7 Seconds : Sixty := 0; 8 Minutes : Sixty := 0; 9 Hours : Twenty_Four := 0; 10 end record; 11 12 procedure Display_Value (C : Clock); 13 procedure Init (C : in out Clock; 14 H : Twenty_Four := 0; 15 M, S : Sixty := 0); 16 17 procedure Increment(C: in out Clock; Inc: Integer :=1); 18 19 type Chronometer is new Clock with null record; 20 21 procedure Display_Value (C : Chronometer); 22 23 end HB.Instruments.Clocks;
Clock extends Instrument. Its operations are Set_Name (inherited from Instrument), Display_Value (overrides the one inherited from Instrument), Init (new primitive), and Increment (new primitive).
Now Chronometer extends Clock. The construct with null record satisfies a rule that a type derived from a tagged type must have an extension, even if no new components are added.
Chronometer has the following operations:
That an extensible type has three kinds of primitive operationsinherited from somewhere above it, overriding inherited ones, and new onesis not unique to Ada, but inherent in the nature of OOP with inheritance, sometimes called classification-first design.
For large industrial-strength type hierarchies, where each object can have many applicable operations, this can lead to real confusion for programmers and for the readers of their programs. For both groups, an important question is Given an object of type T, exactly which operations are applicable to it? Keeping a mental list of these operations is difficult: Some operationsnew and overridingare declared in the same place with the type, but inherited operations could have been declared anywhere above this type in the hierarchy. In comparison, using classical packagessometimes called composition-first designresults in all operations of a given type being declared with the type.
The software industry has recognized that here, as almost everywhere in computing, there is no free lunch in OOP: There is an important tradeoff between the flexibility of classification-first design and the ease of understanding and maintainability in composition-first design. Systems of nontrivial size will probably use both paradigms. Ada supports both paradigms equally well, so the language need not drive the design.
One might ask why Ada requires the use of tagged to indicate an extensible type, that is, why all typesor, at least, all record typesare not potentially extensible. The answer is that extensible types require additional overhead: space for an internal tag, time to distinguish at runtime which of several (potentially many) derived types a given value has, and so forth. Requiring tagged supports a key Ada principle: You dont have to pay for features you dont use.
If a type is not tagged, there will not be the associated overhead. The compiler will not generate such overhead; the human developer is assured that there will be none. This is very important to developers of real-time systems, to whom knowledge of sources of potential runtime overhead is critical. Finally, compiling an Ada 83 program with an Ada 95 compiler produces no new overhead; an Ada 83 program cannot have tagged types because these do not exist in Ada 83!
Previous | Table of Contents | Next |