Object-oriented systems
4.5 Inheritance
4.5.1 Single inheritance
Returning again to our example of the ultrasonic simulation, note that there are two classes of sonic pulses, namely Longitudinal_pulse and Shear_pulse.
While there are some differences between the two classes, there are also many similarities. It would be most unwieldy if all of this common information had to be specified twice. This problem can be avoided by the use of inheritance, sometimes called derivation. A class Sonic_pulse is defined that encompasses
both types of pulses. All of the attributes and methods that are common can be defined here. The classes Longitudinal_pulse and Shear_pulse are then
designated as subclasses or specializations of Sonic_pulse. Conversely,
Sonic_pulse is said to be the superclass or generalization of the other two
classes. The sub/super class relationship can be thought of as is-a-kind-of, i.e., a shear pulse is-a-kind-of sonic pulse. The following expressions, commonly used to describe the is-a-kind-of relationship, are equivalent:
• a subclass is-a-kind-of superclass; • an offspring is-a-kind-of parent; • a derived class is-a-kind-of base class;
• a specialized class is-a-kind-of generalized class; • a specialized class inherits from a generalized class.
In defining a derived class, it is necessary to specify only the name of the base class and the differences from the base class. Attributes and their initial values (where supplied) and methods are inherited by the derived class. (In C++ there are a few exceptions that are not inherited, notably the constructor, destructor, and an overloaded = operator, described in Section 4.11.3.) Any attributes or methods that are redefined in the derived class are said to be
specialized, just as the derived class is said to be a specialization of the base class. Another form of specialization is the introduction of extra attributes and methods in the derived class. C++ gives the user some control over which methods and attributes of the base class are accessible from the derived class and which are not (Section 4.6). Many other OOP languages assume that all attributes and methods of the base class are accessible from the derived class, apart from those that are explicitly specialized.
Figure 4.4 shows the class definitions for Sonic_pulse, its superclasss
Signal, and its subclasses Shear_pulse and Longitudinal_pulse. The attributes graphic, speed, phase, and amplitude are all declared at the highest level class and inherited by the other classes. The class variable
graphic, which determines the screen representation of an instance, is assigned a value at the Sonic_pulse level. This value, as well as the
Longitudinal_pulse speed(m/s) = 3230 move Shear_pulse graphic = "pulse.gif" speed = 5900 phase = 0 amplitude = -20dB position: Coordinate direction: Coordinate attenuate move Sonic_pulse class name attributes graphic : Gif_file speed : Real phase : Real amplitude : Real attenuate animate Signal class attributes instance attributes operations specialized operation specialized attribute specialization relationship
declaration of the attribute, is inherited by the subclasses. Values for the instance variables amplitude and phase are inherited in the same way. Inherited values may be overridden either when an instance is created or subsequently. The class variable speed for Shear_pulse is assigned a different value from the one that would otherwise be inherited.
Operations as well as attributes are inherited. Therefore, instances of
Shear_pulse and Longitudinal_pulse have access to the operations animate
and attenuate. The former is inherited across two generations, while the latter is specialized at the Sonic_pulse class level. The operation move is defined at the Sonic_pulse class level and inherited by Shear_pulse, but redefined for
Longitudinal_pulse.
Figure 4.5 shows the inheritance between other objects in the ultrasonic simulation. Note that the is-a-kind-of relationship is transitive, i.e.:
if x is-a-kind-of y and y is-a-kind-of z, then x is-a-kind-of z.
Therefore, the class Defect, for example, inherits information that is defined at the Feature level and at the General_object level.
4.5.2 Multiple and repeated inheritance
In the example shown in Figure 4.5, each offspring has only one parent. The specialization relationships therefore form a hierarchy. Some OOP languages insist upon hierarchical inheritance. However, others allow an offspring to inherit from more than one parent. This is known as multiple inheritance.
Feature Defect Delay Detector General_object Front_wall Device Transmitter Signal Sonic_pulse Shear_pulse Longitudinal_pulse Back_wall Radio_wave
Where multiple inheritance occurs, the specialization relationships between classes define a network rather than a hierarchy.
Figure 4.6 shows how multiple inheritance might be applied to the ultrasonic simulation. The parts of the component that interact with sonic pulses all inherit from the class Feature. Three of the classes derived from
Feature are required to simulate partial reflection of pulses. This is done by generating one or more reflected pulses, while the existing pulse is propagated in the forward direction with diminished amplitude. Code for generating pulses is contained within the class definition for Transmitter. Multiple inheritance allows those classes that need to generate pulses (Front_wall, Back_wall, and
Defect) to inherit this capability from Transmitter, while inheriting other functions and attributes from Feature.
While multiple inheritance is useful, it can cause ambiguities. This is illustrated in Figure 4.7, where Defect inherits the graphic feature.gif from
Feature and at the same time inherits the graphic transmitter.gif from
Transmitter. This raises two questions. Do the two attributes with the same name refer to the same attribute? If so, the class Defect can have only one value corresponding to the attribute graphic, so which value should be selected? Similarly, Defect inherits conflicting definitions for the operation
send_pulse.
The most reliable way to resolve such conflicts is to have them detected by the language compiler, so that the programmer can then state explicitly the intended meaning. Some OOP environments allow the user to set a default strategy for resolving conflicts. An example might be to give preference to the
Feature Defect Delay Detector General_object Front_wall Device Transmitter Signal Sonic_pulse Shear_pulse Longitudinal_pulse Back_wall Radio_wave
parent class that is either closest to, or furthest from, the root of the inheritance tree. This would not help with the example in Figure 4.7 as the two parents are equidistant from the root. Alternatively the class names may be deemed significant in some way, or preference may be given to the most recently defined specialization.
A further problem is that one class may find itself indirectly inheriting from another via more than one route, as shown in Figure 4.8. This is known as repeated inheritance. Although the meaning may be clear to the programmer, an OOP language must have some strategy for recognizing and dealing with repeated inheritance, if it allows it at all. C++ offers the programmer a choice of two strategies. Class D can have two copies of Class A, one for each inheritance route. Alternatively it can have just a single copy, if both Class B and Class C are declared to have Class A as a virtual base class.
Multiple inheritance gives rise to the idea of mixins, which are classes designed solely to help organize the inheritance structure. Instances of mixins cannot be created. Consider, for example, a class hierarchy for engineering materials. The materials polyethylene, Bakelite, gold, steel, and silicon nitride can be classified as polymers, metals, or ceramics. Single inheritance would allow us to construct these hierarchical relationships. Under multiple inheritance we can categorize the materials in a variety of ways at the same
Transmitter send_pulse propagate interact graphic = "feature.gif" send_pulse display generate_pulse Feature severity: Integer display propagate interact reflect Defect graphic = "transmitter.gif"
Figure 4.7 Conflicts arising from multiple inheritance: each parent has a different value for graphic and a different definition for send_pulse
time. Figure 4.9 shows the use of the mixins Cheap and Brittle for this purpose.
4.5.3 Specialization of methods
We have already seen that specialization can involve the introduction of new methods or attributes, overriding default assignments to attributes, or
Class_A
Class_B Class_C
Class_D
Figure 4.8 Repeated inheritance
Polymer Steel Material Metal Gold Ceramic Silicon_nitride Bakelite Polyethylene Cheap Brittle
redefinition of inherited operations. If an operation needs to be redefined, i.e., specialized, it is not always necessary to rewrite it from scratch. In our example, a definition of the method propagate is inherited by the class Defect
from the class Feature. The specialized version of propagate is the same, except that the sonic pulse must be attenuated. This can be achieved by calling the inherited definition from within the specialized one, and then adding the attenuation instruction, shown here in C++:
void Defect::propagate(Sonic_pulse* inst1) {
Feature::propagate(inst1);
// propagate a pulse, inst1, using inherited version of // 'propagate'
inst1->attenuate(0.5); // Attenuate the pulse.
// The member function 'attenuate' must be defined for the // class 'Sonic_pulse'.
}
We might wish to call the specialized method propagate from within the definition of interact, a function which handles the overall interaction of a pulse with a defect:
void Defect::interact(Sonic_pulse* inst1) {
propagate(inst1);
// propagate a pulse, inst1, using the locally defined // member function
reflect(inst1);
// generate a new pulse and send it in the opposite // direction
}
4.5.4 Browsers
Many OOP systems provide not only a language, but an environment in which to program. The environment may include tools that make OOP easier, and one of the most important of these tools is a class-browser. A class-browser shows inheritance relationships, often in the form of a tree such as those in Figures 4.5 and 4.6. If the programmer wants to alter a class or to specialize it to form a new one, he or she simply chooses the class from the browser and then selects the type of change from a menu. Incidentally, browsers themselves are invariably built using OOP. Thus, the class-browser may be an instance of a class called Class_browser, which may be a specialization of Browser, itself a specialization of Window. The class Class_browser could be further specialized to provide different display or editing facilities.