14 Introduction to inheritance nteresting systems are seldom born into an empty world. Almost always,new software expands on previous developments;the best way to create it is by imitation,refinement and combination.Traditional design methods largely ignored this aspect of system development.In object technology it is an essential concern. The techniques studied so far are not enough.Classes do provide a good modular decomposition technique and possess many of the qualities expected of reusable components:they are homogeneous,coherent modules;you may clearly separate their interface from their implementation according to the principle of information hiding, genericity gives them some flexibility;and you may specify their semantics precisely thanks to assertions.But more is needed to achieve the full goals of reusability and extendibility. For reusability,any comprehensive approach must face the problem of repetition and variation,analyzed in an earlier chapter.To avoid rewriting the same code over and over again,wasting time,introducing inconsistencies and risking errors,we need techniques to capture the striking commonalities that exist within groups of similar structures-all text editors,all tables,all file handlers-while accounting for the many differences that characterize individual cases. For extendibility,the type system described so far has the advantage of guaranteeing type consistency at compile time,but prohibits combination of elements of diverse forms even in legitimate cases.For example,we cannot yet define an array containing geometrical objects of different but compatible types such as POINT and SEGMENT. Progress in either reusability or extendibility demands that we take advantage of the strong conceptual relations that hold between classes:a class may be an extension, specialization or combination of others.We need support from the method and the language to record and use these relations.Inheritance provides this support. A central and fascinating component of object technology,inheritance will require several chapters.In the present one we discover the fundamental concepts.The next three chapters will describe more advanced consequences:multiple inheritance,renaming, subcontracting,influence on the type system.Chapter 24 complements these technical presentations by providing the methodological perspective:how to use inheritance,and avoid misusing it
14 Introduction to inheritance Interesting systems are seldom born into an empty world. Almost always, new software expands on previous developments; the best way to create it is by imitation, refinement and combination. Traditional design methods largely ignored this aspect of system development. In object technology it is an essential concern. The techniques studied so far are not enough. Classes do provide a good modular decomposition technique and possess many of the qualities expected of reusable components: they are homogeneous, coherent modules; you may clearly separate their interface from their implementation according to the principle of information hiding; genericity gives them some flexibility; and you may specify their semantics precisely thanks to assertions. But more is needed to achieve the full goals of reusability and extendibility. For reusability, any comprehensive approach must face the problem of repetition and variation, analyzed in an earlier chapter. To avoid rewriting the same code over and over again, wasting time, introducing inconsistencies and risking errors, we need techniques to capture the striking commonalities that exist within groups of similar structures — all text editors, all tables, all file handlers — while accounting for the many differences that characterize individual cases. For extendibility, the type system described so far has the advantage of guaranteeing type consistency at compile time, but prohibits combination of elements of diverse forms even in legitimate cases. For example, we cannot yet define an array containing geometrical objects of different but compatible types such as POINT and SEGMENT. Progress in either reusability or extendibility demands that we take advantage of the strong conceptual relations that hold between classes: a class may be an extension, specialization or combination of others. We need support from the method and the language to record and use these relations. Inheritance provides this support. A central and fascinating component of object technology, inheritance will require several chapters. In the present one we discover the fundamental concepts. The next three chapters will describe more advanced consequences: multiple inheritance, renaming, subcontracting, influence on the type system. Chapter 24 complements these technical presentations by providing the methodological perspective: how to use inheritance, and avoid misusing it
460 INTRODUCTION TO INHERITANCE $14.1 14.1 POLYGONS AND RECTANGLES To master the basic concepts we will use a simple example.The example is sketched rather than complete,but it shows the essential ideas well. Polygons Assume we want to build a graphics library.Classes in this library will describe geometrical abstractions:points,segments,vectors,circles,ellipses,general polygons, triangles,rectangles,squares and so on. Consider first the class describing general polygons.Operations will include computation of the perimeter,translation,rotation.The class may look like this: indexing description:"Polygons with an arbitrary number of vertices" class POLYGON creation feature--Access count:INTEGER --Number of vertices perimeter:REAL is --Length of perimeter do...end feature--Transformation display is --Display polygon on screen. do...end rotate (center:POINT:angle:REAL)is --Rotate by angle around center. do ..See next... end translate (a,b:REAL)is -Move by a horizontally,b vertically. do...end ..Other feature declarations .. feature (NONE--Implementation vertices:LINKED LIST [POINT] --Successive points making up polygon invariant same count as implementation:count vertices.count at least three:count>=3 --A polygon has at least three vertices (see exercise 14.2) end The attribute vertices yields the list ofvertices;the choice ofa linked list is only one See also exercise possible implementation.(An array might be better.) E24.4,page869
460 INTRODUCTION TO INHERITANCE §14.1 14.1 POLYGONS AND RECTANGLES To master the basic concepts we will use a simple example. The example is sketched rather than complete, but it shows the essential ideas well. Polygons Assume we want to build a graphics library. Classes in this library will describe geometrical abstractions: points, segments, vectors, circles, ellipses, general polygons, triangles, rectangles, squares and so on. Consider first the class describing general polygons. Operations will include computation of the perimeter, translation, rotation. The class may look like this: indexing description: "Polygons with an arbitrary number of vertices" class POLYGON creation … feature -- Access count: INTEGER -- Number of vertices perimeter: REAL is -- Length of perimeter do … end feature -- Transformation display is -- Display polygon on screen. do … end rotate (center: POINT; angle: REAL) is -- Rotate by angle around center. do … See next … end translate (a, b: REAL) is -- Move by a horizontally, b vertically. do … end … Other feature declarations … feature {NONE} -- Implementation vertices: LINKED_LIST [POINT] -- Successive points making up polygon invariant same_count_as_implementation: count = vertices ● count at_least_three: count >= 3 -- A polygon has at least three vertices (see exercise 14.2) end The attribute vertices yields the list of vertices; the choice of a linked list is only one possible implementation. (An array might be better.) See also exercise E24.4, page 869
$14.1 POLYGONS AND RECTANGLES 461 Here is a possible implementation for a typical procedure,rotate.The procedure performs a rotation by a certain angle around a certain rotation center.To rotate a polygon, it suffices to rotate every vertex in turn: rotate (center:POINT;angle:REAL)is --Rotate around center by angle. do from vertices.start until vertices.after loop vertices.item.rotate (center,angle) vertices.forth end end The text of class To understand this procedure,note that feature item from LINKED LIST yields the POINT appeared on value of the currently active list element(where the cursor is).Since vertices is of type page 176. LINKED LIST [POINT],vertices.item denotes a point,to which we may apply procedure rotate defined for class POINT in an earlier chapter.It is valid-and common-to give the same name,here rotate,to features of different classes,as the target of any feature always has a clearly defined type.(This is the -O form of overloading.) Another routine,more important for our immediate purposes,is the function to compute the perimeter of a polygon.Since our polygons have no special properties,the only way to compute their perimeter is to loop through their vertices and sum the edge lengths.Here is an implementation of perimeter: perimeter:REAL is --Sum of edge lengths local this,previous:POINT do from vertices.start,this vertices.item check not vertices.after end--A consequence ofat least three until vertices.is last this (is last) loop previous :this previous vertices.forth first this :vertices.item Result:Result this.distance (previous) start) end ResultResult this.distance (vertices.first) end
§14.1 POLYGONS AND RECTANGLES 461 Here is a possible implementation for a typical procedure, rotate. The procedure performs a rotation by a certain angle around a certain rotation center. To rotate a polygon, it suffices to rotate every vertex in turn: rotate (center: POINT; angle: REAL) is -- Rotate around center by angle. do from vertices ● start until vertices ● after loop vertices ● item ● rotate (center, angle) vertices ● forth end end To understand this procedure, note that feature item from LINKED_LIST yields the value of the currently active list element (where the cursor is). Since vertices is of type LINKED_LIST [POINT], vertices ● item denotes a point, to which we may apply procedure rotate defined for class POINT in an earlier chapter. It is valid — and common — to give the same name, here rotate, to features of different classes, as the target of any feature always has a clearly defined type. (This is the O-O form of overloading.) Another routine, more important for our immediate purposes, is the function to compute the perimeter of a polygon. Since our polygons have no special properties, the only way to compute their perimeter is to loop through their vertices and sum the edge lengths. Here is an implementation of perimeter: perimeter: REAL is -- Sum of edge lengths local this, previous: POINT do from vertices ● start; this := vertices ● item check not vertices ● after end -- A consequence of at_least_three until vertices ● is_last loop previous := this vertices ● forth this := vertices ● item Result := Result + this ● distance (previous) end Result := Result + this ● distance (vertices ● first) end The text of class POINT appeared on page 176. this previous (start) (is_last) first
462 INTRODUCTION TO INHERITANCE $14.1 The loop simply adds the successive distances between adjacent vertices.Function The list interface will distance was defined in class POINT.Result,representing the value to be returned by the be discussed in function,is automatically initialized to 0 on routine entry.From class LINKED L/ST we “ACTIVE DATA use features first to get the first element,start to move the cursor to that first element, STRUCTURES” forth to advance it to the next,item to get the value of the element at cursor position,is 23.4,page774. last to know whether the current element is the last one,afier to know if the cursor is past the last element.As recalled by the check instruction the invariant clause at least_three will guarantee that the loop starts and terminates properly:since it starts in a not afier state,vertices.item is defined,and applying forth one or more time is correct and will eventually yield a state satisfying is_last,the loop's exit condition. Rectangles Now assume we need a new class representing rectangles.We could start from scratch. But rectangles are a special kind of polygon and many of the features are the same:a rectangle will probably be translated,rotated or displayed in the same way as a general polygon.Rectangles,on the other hand,also have special features (such as a diagonal), special properties(the number of vertices is four,the angles are right angles),and special versions of some operations (to compute the perimeter of a rectangle,we can do better than the above general poly gon algorithm). We can take advantage of this mix of commonality and specificity by defining class RECTANGLE as an heir to class POLYGON.This makes all the features of POLYGON -called a parent of RECTANGLE-by default applicable to the heir class as well.It suffices to give RECTANGLE an inheritance clause: elass RECTANGLE inherit POLYGON feature ..Features specific to rectangles... end The feature clause of the heir class does not repeat the features of the parent:they are automatically available because of the inheritance clause.It will only list features that are specific to the heir.These may be new features,such as diagonal;but they may also be redefinitions of inherited features. The second possibility is useful for a feature that was already meaningful for the parent but requires a different form in the heir.Consider perimeter.It has a better implementation for rectangles:no need to compute four vertex-to-vertex distances;the result is simply twice the sum of the two side lengths.An heir that redefines a feature for the parent must announce it in the inheritance clause through a redefine subclause: elass RECTANGLE inherit POLYGON redefine perimeter end feature end
462 INTRODUCTION TO INHERITANCE §14.1 The loop simply adds the successive distances between adjacent vertices. Function distance was defined in class POINT. Result, representing the value to be returned by the function, is automatically initialized to 0 on routine entry. From class LINKED_LIST we use features first to get the first element, start to move the cursor to that first element, forth to advance it to the next, item to get the value of the element at cursor position, is_ last to know whether the current element is the last one, after to know if the cursor is past the last element. As recalled by the check instruction the invariant clause at_least_three will guarantee that the loop starts and terminates properly: since it starts in a not after state, vertices ● item is defined, and applying forth one or more time is correct and will eventually yield a state satisfying is_last, the loop’s exit condition. Rectangles Now assume we need a new class representing rectangles. We could start from scratch. But rectangles are a special kind of polygon and many of the features are the same: a rectangle will probably be translated, rotated or displayed in the same way as a general polygon. Rectangles, on the other hand, also have special features (such as a diagonal), special properties (the number of vertices is four, the angles are right angles), and special versions of some operations (to compute the perimeter of a rectangle, we can do better than the above general polygon algorithm). We can take advantage of this mix of commonality and specificity by defining class RECTANGLE as an heir to class POLYGON. This makes all the features of POLYGON — called a parent of RECTANGLE — by default applicable to the heir class as well. It suffices to give RECTANGLE an inheritance clause: class RECTANGLE inherit POLYGON feature … Features specific to rectangles … end The feature clause of the heir class does not repeat the features of the parent: they are automatically available because of the inheritance clause. It will only list features that are specific to the heir. These may be new features, such as diagonal; but they may also be redefinitions of inherited features. The second possibility is useful for a feature that was already meaningful for the parent but requires a different form in the heir. Consider perimeter. It has a better implementation for rectangles: no need to compute four vertex-to-vertex distances; the result is simply twice the sum of the two side lengths. An heir that redefines a feature for the parent must announce it in the inheritance clause through a redefine subclause: class RECTANGLE inherit POLYGON redefine perimeter end feature … end The list interface will be discussed in “ACTIVE DATA STRUCTURES”, 23.4, page 774
$14.1 POLYGONS AND RECTANGLES 463 This allows the feature clause of RECTANGLE to contain a new version of perimeter,which will supersede the POLYGON version for rectangles.If the redefine subclause were not present,a new declaration of perimeter among the features of RECTANGLE would be an error:since RECTANGLE already has a perimeter feature inherited from POLYGON,this would amount to declaring a feature twice. The RECTANGLE class looks like the following: indexing description:"Rectangles,viewed as a special case of general polygons" class RECTANGLE inherit POLYGON redefine perimeter end creation make feature --Initialization make (center:POINT;s1,s2,angle:REAL)is --Set up rectangle centered at center,with side lengths --s/and s2 and orientation angle. do ..end feature--Access sidel,side2:REAL --The two side lengths diagonal:REAL --Length of the diagonal perimeter:REAL is --Sum of edge lengths --(Redefinition of the POLYGON version) do Result :2 (sidel side2) end side? sidel invariant four sides:count =4 For a list,i th (i) first side:(vertices.i th(1)).distance (vertices.i th(2))=sidel gives the element at second side:(vertices.i th (2)).distance (vertices.i th(3))=side2 position i (the i-th element,hence the third side:(vertices.i th (3)).distance (vertices.i th (4))=sidel name of the query). fourth side:(vertices.i th (4)).distance (vertices.i th(1)=side2 end Because RECTANGLE is an heir of POLYGON,all features of the parent class are still applicable to the new class:vertices,rotate,translate,perimeter (in redefined form) and any others.They do not need to be repeated in the new class. This process is transitive:any class that inherits from RECTANGLE,say SOUARE, also has the POLYGON features
§14.1 POLYGONS AND RECTANGLES 463 This allows the feature clause of RECTANGLE to contain a new version of perimeter, which will supersede the POLYGON version for rectangles. If the redefine subclause were not present, a new declaration of perimeter among the features of RECTANGLE would be an error: since RECTANGLE already has a perimeter feature inherited from POLYGON, this would amount to declaring a feature twice. The RECTANGLE class looks like the following: indexing description: "Rectangles, viewed as a special case of general polygons" class RECTANGLE inherit POLYGON redefine perimeter end creation make feature -- Initialization make (center: POINT; s1, s2, angle: REAL) is -- Set up rectangle centered at center, with side lengths -- s1 and s2 and orientation angle. do … end feature -- Access side1, side2: REAL -- The two side lengths diagonal: REAL -- Length of the diagonal perimeter: REAL is -- Sum of edge lengths -- (Redefinition of the POLYGON version) do Result := 2 ✳ (side1 + side2) end invariant four_sides: count = 4 first_side: (vertices ● i_th (1)) ● distance (vertices ● i_th (2)) = side1 second_side: (vertices ● i_th (2)) ● distance (vertices ● i_th (3)) = side2 third_side: (vertices ● i_th (3)) ● distance (vertices ● i_th (4)) = side1 fourth_side: (vertices ● i_th (4)) ● distance (vertices ● i_th (1)) = side2 end Because RECTANGLE is an heir of POLYGON, all features of the parent class are still applicable to the new class: vertices, rotate, translate, perimeter (in redefined form) and any others. They do not need to be repeated in the new class. This process is transitive: any class that inherits from RECTANGLE, say SQUARE, also has the POLYGON features. 1 2 4 3 side1 side2 For a list, i_th (i) gives the element at position i (the i-th element, hence the name of the query)