Chapter 8. Inheritance
The problem: You write a class C and later discover a class D that is almost identical to C but has some extra attributes and operations.
You could just duplicate C's operations and attributes into D, but: This is extra work and make maintenance difficult
Better solution: Ask class D to re-use operations / attributes of C. This is called inheritance.
Important
- inheritance
Inheritance (by D from C) is the facility by which a class D has impliclity defined upon it each of the attributes and operations of class C, as if those attributes and operations had been defined upon D itself.
- superclass
C is a superclass of D.
- subclass
D is a subclass of C.
Through inheritance, objects of class D can make use of attributes and operations that where defined in class C (and D).
Inheritance allows to build software incrementally, allowing you to:
First build classes for the generic case
Then build classes for special cases that inherit from the generic classes.
Example:
Class Aircraft which defines an operation turn() and an instance attribute course. This works well for the all air crafts.
However, there may be special aircraft, such as glider. A glider need to know its course and be able to change it, but it also needs special operations (e.g. release towline) and special attributes (e.g. isTowlineAttached).
We can have Glider inherit from Aircraft. Then Glider will have all its instance attributes and operations and the ones from Aircraft.
The glider now has the attributes:
course
towLineAttached
and the operations:
turn()
releaseTowline()
Notes for the UML notation:
inheritance is shown with a directed arrow.
the arrowhead must be an unfilled triangle!
MagicDraw calls this "Generalization".
Practice: Model a cable box and a dvr cable box. The cable box has a current channel, which can be set with an "channelUp" and a "channelDown" button. A dvr cable box has all the features of a regular cable box, but also has a "record" button and has a certain number of minutes left for recording. Draw a UML diagram for both classes, using inheritance.
But lets look again at the aircraft / glider example and some sample code:
var ac: Aircraft := Aircraft.New; var gl: Glider := Glider.New; ... ac.turn(newCourse, out turnOk); gl.releaseTowline; gl.turn(newCourse, out turnOk); ac.releaseTowline; ...
Or the same in C++ notation:
Aircraft *ac = new Aircraft(); Glider *gl = new Glider(); ... turnOk = ac->turn(newCourse); gl->releaseTowline(); turnOk = gl->turn(newCourse); ac->releaseTowline(); ...
The object pointed to by ac receives the message "turn", which causes it to apply the operation turn(). Since ac is an instance of Aircraft, it will use the operation turn() defined on the class Aircraft. |
|
The object pointed to by gl receives the message "releaseTowline" which causes it to apply the operation releaseTowline(). Since gl is an instance of Glider, it will use the operation releaseTowline() defined on the class Glider. |
|
The object pointed to by gl received the message "turn", which causes it to apply the operation turn(). Glider does not define turn(), but since Aircraft is a superclass of Glider, it will use the operation turn() defined on the class Aircraft. |
|
This will not work! ac refers to an instance of Aircraft, but Aircraft does not have an operation "releaseTowline". Inheritance does not help in this case! |
Distinction between object and instance:
Any object is an instance of its class and of all of its classes superclasses!
ac is an object of class Aircraft. ac is an instance of class Aircraft.
gl is an object of class Glider. gl is an instance of the classes Aircraft and Glider.
Compare to real world: If you own a glider, you own an aircraft at the same time (even though it is the same object).
is-a relationship: Inhertiance is usually useful whenever an is-a relationship can be used. Example: Every glider is-an aircraft.
Common mistake: Using inheritance instead of attributes. For example, every Hominoid has a location. So why not just inherit from Location? Then Hominoid would have all the attributes and operations needed for it's location, right? Technically - Yes. Logically, is every Hominoid also a Location? No! A Hominoid has a location. Therefore, also technically feasable this is bad design!
Inheritance can span multiple levels. Example: FlyingThing, AirCraft, Glider. Every glider is an aircraft and a flying thing. Every aircraft is a flying thing.
Every class may have multiple subclasses! Example: a Boing 747 is an aircraft, a Glider is an Aircraft.
Practice: Draw a larger inheritance diagram (use short class notation), for the classes:
Motorcycle
Helicopter
StreetVehicle
Car
Aircraft
Plane
-
Vehicle
Figure 8.4. Example solution for vehicle inheritance
Multiple Inheritance
So far we have seen single-inheritance: Each class had only one direct superclass. However, each class could have multiple subclasses. This can be shown in an inheritance tree (The example we have seen so far).
But can a class also have multiple (direct) superclasses? Yes, this is called "multiple inheritance".
Warning! Multiple inheritance can cause mayor design problems ( = headaches). Why? what happens if operations or attributes "clash"?
Example: Assume Aircraft defines a "size" attribute which defines the length of the aircraft in feet (float). PassengerVehicle defines a "size" atrribute which defines how many passenger fit in this vehicle (int). When Boing747 inherits the "size" attribute, what does it mean?
As a matter of fact, multiple inheritance can cause so many problems that some languages forbid it! C++ and Eiffel allow it, Java, Delphi, ObjC and Smalltalk do not. (Java / Delphi / ObjC have a workaround with interfaces / protocols, but more about that later).
Multiple inheritance, if used, should be used very cautiously! Only use it if there is no other way!