814 USING INHERITANCE PROPERLY $24.2 The reverse is not true:when"has"is legitimate,"is"is not always applicable,as the CAR OWNER example shows so clearly.This observation takes care ofthe easy mistakes, obvious to anyone having understood the basic concepts,and perhaps even explainable to authors of undergraduate texts.But whenever"is"does apply it is not the only contender. So two reasonable and competent people may disagree,one wanting to use inheritance, the other preferring client. Two criteria fortunately exist to help in such discussions.Not surprisingly (since they address a broad design issue)they may sometimes fail to give a clear,single solution. But in many practical cases they do tell you,beyond any hesitation,which of the two relations is the right one. Conveniently,one of these two criteria favors inheritance,and the other favors client. The rule of change The first observation is that the client relation usually permits change,while the inheritance relation does not.Here we must be careful with our use of the verbs"to be" and"to have"from ordinary language;so far they have helped us characterize the general nature of our two software relations,but software rules are,as always,more precise than their general non-software counterparts. One of the defining properties of inheritance is that it is a relation between classes, not objects.We have interpreted the property "Class B inherits from class 4"as meaning "every B object is an A object",but must remember that it is not in the power of any such object to change that property:only a change of the class can achieve such a result.The property characterizes the software,not any particular execution. With the client relation,the constraints are looser.If an object of type B has a component of type (either a subobject or an object reference),it is quite possible to change that component;the only restrictions are those of the type system,ensuring provably reliable execution(and govemned,through an interesting twist,by the inheritance structure) So even though a given inter-object relationship can result from either inheritance or client relationships between the corresponding classes,the effect will be different as to what can be changed and what cannot.For example our fictitious object structure Object and subobject (ENGINEER) (Other components omitted) (SOFTWARE ENGINEER)
814 USING INHERITANCE PROPERLY §24.2 The reverse is not true: when “has” is legitimate, “is” is not always applicable, as the CAR_OWNER example shows so clearly. This observation takes care of the easy mistakes, obvious to anyone having understood the basic concepts, and perhaps even explainable to authors of undergraduate texts. But whenever “is” does apply it is not the only contender. So two reasonable and competent people may disagree, one wanting to use inheritance, the other preferring client. Two criteria fortunately exist to help in such discussions. Not surprisingly (since they address a broad design issue) they may sometimes fail to give a clear, single solution. But in many practical cases they do tell you, beyond any hesitation, which of the two relations is the right one. Conveniently, one of these two criteria favors inheritance, and the other favors client. The rule of change The first observation is that the client relation usually permits change, while the inheritance relation does not. Here we must be careful with our use of the verbs “to be” and “to have” from ordinary language; so far they have helped us characterize the general nature of our two software relations, but software rules are, as always, more precise than their general non-software counterparts. One of the defining properties of inheritance is that it is a relation between classes, not objects. We have interpreted the property “Class B inherits from class A” as meaning “every B object is an A object”, but must remember that it is not in the power of any such object to change that property: only a change of the class can achieve such a result. The property characterizes the software, not any particular execution. With the client relation, the constraints are looser. If an object of type B has a component of type A (either a subobject or an object reference), it is quite possible to change that component; the only restrictions are those of the type system, ensuring provably reliable execution (and governed, through an interesting twist, by the inheritance structure). So even though a given inter-object relationship can result from either inheritance or client relationships between the corresponding classes, the effect will be different as to what can be changed and what cannot. For example our fictitious object structure Object and subobject (SOFTWARE_ENGINEER) (ENGINEER) (Other components omitted)
$24.2 WOULD YOU RATHER BUY OR INHERIT? 815 could result from an inheritance relationship between the corresponding classes: class SOFTWARE ENGINEER I inherit ENGINEER ENGINEER feature 44 end--class SOFTWARE ENGINEER I SOFTWARE ENGINEER but it could just as well have been obtained through the client relation: class SOFTWARE ENGINEER 2 feature the_engineer in me:ENGINEER end--class SOFTWARE ENGINEER 2 which could in fact be class SOFTWARE ENGINEER 3 feature 、SOFTWARE ENGINEER3 the truly important part of me:VOCATION VOCATION end--class SOFTWARE ENGINEER 3 provided we satisfy the type rules by making class ENGINEER a descendant of class VOCATION. Strictly speaking the last two variants represent a slightly different situation from the first if we assume that none of the given classes is expanded:instead of subobjects,the "software engineer"objects will in the last two cases contain references to "engineer" objects,as in the second figure of page 813.The introduction of references,however, does not fundamentally affect this discussion. With the first class definition,because the inheritance relationship holds between the generating classes,it is not possible to modify the object relationship dynamically:once an engineer,always an engineer. But with the other two definitions such a modification is possible:a procedure of the "software engineer"class can assign a new value to the corresponding object field (the field for the engineer in me or the truly important part of me).In the case of class SOFTWARE ENGINEER 2 the new value must be of type ENGINEER or compatible; but with class SOFTWARE ENGINEER 3 it may be of any type compatible with VOCAT/ON.So our software can model the idea of a software engineer who,after many years of pretending to be an engineer,finally sheds that part of his personality in favor of something that he deems more representative of his work,such as poet or plumber
§24.2 WOULD YOU RATHER BUY OR INHERIT? 815 could result from an inheritance relationship between the corresponding classes: class SOFTWARE_ENGINEER_1 inherit ENGINEER feature … end -- class SOFTWARE_ENGINEER_1 but it could just as well have been obtained through the client relation: class SOFTWARE_ENGINEER_2 feature the_engineer_in_me: ENGINEER … end -- class SOFTWARE_ENGINEER_2 which could in fact be class SOFTWARE_ENGINEER_3 feature the_truly_important_part_of_me: VOCATION … end -- class SOFTWARE_ENGINEER_3 provided we satisfy the type rules by making class ENGINEER a descendant of class VOCATION. Strictly speaking the last two variants represent a slightly different situation from the first if we assume that none of the given classes is expanded: instead of subobjects, the “software engineer” objects will in the last two cases contain references to “engineer” objects, as in the second figure of page 813. The introduction of references, however, does not fundamentally affect this discussion. With the first class definition, because the inheritance relationship holds between the generating classes, it is not possible to modify the object relationship dynamically: once an engineer, always an engineer. But with the other two definitions such a modification is possible: a procedure of the “software engineer” class can assign a new value to the corresponding object field (the field for the_engineer_in_me or the_truly_important_part_of_me). In the case of class SOFTWARE_ENGINEER_2 the new value must be of type ENGINEER or compatible; but with class SOFTWARE_ENGINEER_3 it may be of any type compatible with VOCATION. So our software can model the idea of a software engineer who, after many years of pretending to be an engineer, finally sheds that part of his personality in favor of something that he deems more representative of his work, such as poet or plumber. ENGINEER SOFTWARE_ENGINEER_1 SOFTWARE_ENGINEER_3 VOCATION
816 USING INHERITANCE PROPERLY $24.2 This yields our first criterion: Rule of change Do not use inheritance to describe a perceived "is-a"relation if the corresponding object components may have to be changed at run time. Only use inheritance if the corresponding inter-object relation is permanent.In other cases,use the client relation. The really interesting case is the one illustrated by SOFTWARE ENGINEER 3.With SOFTWARE ENGINEER 2 you can only replace the engineer component with another ofexactly same type.But in the SOFTWARE_ENGINEER_3 scheme,IOCATION should be a high-level class,most likely deferred;so the attribute can(through polymorphism) represent objects of many possible types,all conforming to IOCATION. This also means that even though this solution uses client as the primary relation,in practice its final form will often use inheritance as a complement.This will be particularly clear when we come to the notion of handle. The polymorphism rule Now for a criterion that will require inheritance and exclude client.That criterion is simple:polymorphic uses.In our study of inheritance we have seen that with a declaration of the form x:C x denotes at run time (assuming class C is not expanded)a potentially polymorphic reference;that is to say,x may become attached to direct instances not just of C but ofany proper descendants of C.This property is of course a key contribution to the power and flexibility of the object-oriented method,especially through its corollary,the possibility of defining polymorphic data structures,such as a LIST[C]which may contains instances of any of C's descendants. In our example,this means that with the SOFTWARE ENGINEER solution-the form of the class which inherits from ENGINEER-a client can declare an entity eng:ENGINEER which may become attached at run time to an object of type SOFTWARE ENGINEER 1. Or we can have a list of engineers,or a database of engineers,which includes a few mechanical engineers,a few chemical engineers,and a few software engineers as well. A reminder on methodology:the use of non-software words is a good help for understanding the concepts,but we should not let ourselves get carried away by such anthropomorphic examples;the objects of interest are software objects.So although we may loosely understand the words"a software engineer"for what they say,they actually denote an instance of SOFTWARE ENGINEER 1,that is to say,a software object somehow modeling a real person
816 USING INHERITANCE PROPERLY §24.2 This yields our first criterion: Only use inheritance if the corresponding inter-object relation is permanent. In other cases, use the client relation. The really interesting case is the one illustrated by SOFTWARE_ENGINEER_3. With SOFTWARE_ENGINEER_2 you can only replace the engineer component with another of exactly same type. But in the SOFTWARE_ENGINEER_3 scheme, VOCATION should be a high-level class, most likely deferred; so the attribute can (through polymorphism) represent objects of many possible types, all conforming to VOCATION. This also means that even though this solution uses client as the primary relation, in practice its final form will often use inheritance as a complement. This will be particularly clear when we come to the notion of handle. The polymorphism rule Now for a criterion that will require inheritance and exclude client. That criterion is simple: polymorphic uses. In our study of inheritance we have seen that with a declaration of the form x: C x denotes at run time (assuming class C is not expanded) a potentially polymorphic reference; that is to say, x may become attached to direct instances not just of C but of any proper descendants of C. This property is of course a key contribution to the power and flexibility of the object-oriented method, especially through its corollary, the possibility of defining polymorphic data structures, such as a LIST [C] which may contains instances of any of C’s descendants. In our example, this means that with the SOFTWARE_ENGINEER_1 solution — the form of the class which inherits from ENGINEER — a client can declare an entity eng: ENGINEER which may become attached at run time to an object of type SOFTWARE_ENGINEER_1. Or we can have a list of engineers, or a database of engineers, which includes a few mechanical engineers, a few chemical engineers, and a few software engineers as well. A reminder on methodology: the use of non-software words is a good help for understanding the concepts, but we should not let ourselves get carried away by such anthropomorphic examples; the objects of interest are software objects. So although we may loosely understand the words “a software engineer” for what they say, they actually denote an instance of SOFTWARE_ENGINEER_1, that is to say, a software object somehow modeling a real person. Rule of change Do not use inheritance to describe a perceived “is-a” relation if the corresponding object components may have to be changed at run time
$24.3 AN APPLICATION:THE HANDLE TECHNIQUE 817 Such polymorphic effects require inheritance:with SOFTWARE ENGINEER 2 or SOFTWARE ENGINEER 3 there is no way an entity or data structure of type ENGINEER can directly denote"software engineer"objects. Generalizing these observations-which are not,of course,specific to the example -yields the complement of the rule of change: Polymorphism rule Inheritance is appropriate to describe a perceived"is-a"relation if entities or data structure components of the more general type may need to become attached to objects of the more specialized type. Summary Although it brings no new concept,the following rule will be convenient as a summary of this discussion of criteria for and against inheritance. Choosing between client and inheritance In deciding how to express the dependency of a class B on a class A,apply the following criteria: CIl.If every instance of B initially has a component of type A,but that component may need to be replaced at run time by an object of a different type,make B a client of A. CI2.If there is a need for entities of type 4 to denote objects of type B, or for polymorphic structures containing objects of type 4 of which some may be of type B,make B an heir of 4. 24.3 AN APPLICATION:THE HANDLE TECHNIQUE Here is an example using the preceding rule.It yields a design pattern of wide applicability:handles. The first design of the Vision library for platform-independent graphics encountered a general problem:how to account for platform dependencies.The first solution used multiple inheritance in the following way:a typical class,such as the one describing windows,would have a parent describing the platform-independent properties of the corresponding abstraction,and another providing the platform-specific elements. elass WINDOW inherit GENERAL WINDOW PLATFORM WINDOW feature end -class WINDOW
§24.3 AN APPLICATION: THE HANDLE TECHNIQUE 817 Such polymorphic effects require inheritance: with SOFTWARE_ENGINEER_2 or SOFTWARE_ENGINEER_3 there is no way an entity or data structure of type ENGINEER can directly denote “software engineer” objects. Generalizing these observations — which are not, of course, specific to the example — yields the complement of the rule of change: Summary Although it brings no new concept, the following rule will be convenient as a summary of this discussion of criteria for and against inheritance. 24.3 AN APPLICATION: THE HANDLE TECHNIQUE Here is an example using the preceding rule. It yields a design pattern of wide applicability: handles. The first design of the Vision library for platform-independent graphics encountered a general problem: how to account for platform dependencies. The first solution used multiple inheritance in the following way: a typical class, such as the one describing windows, would have a parent describing the platform-independent properties of the corresponding abstraction, and another providing the platform-specific elements. class WINDOW inherit GENERAL_WINDOW PLATFORM_WINDOW feature … end -- class WINDOW Polymorphism rule Inheritance is appropriate to describe a perceived “is-a” relation if entities or data structure components of the more general type may need to become attached to objects of the more specialized type. Choosing between client and inheritance In deciding how to express the dependency of a class B on a class A, apply the following criteria: CI1 • If every instance of B initially has a component of type A, but that component may need to be replaced at run time by an object of a different type, make B a client of A. CI2 • If there is a need for entities of type A to denote objects of type B, or for polymorphic structures containing objects of type A of which some may be of type B, make B an heir of A
818 USING INHERITANCE PROPERLY $24.3 Platform GENERAL PLATFORM adaptation WINDOW WINDOW through inheritance WINDOW Class GENERAL WINDOW and similar ones such as GENERAL BUTTON are On the platform- deferred:they express all that can be said about the corresponding graphical objects and specific libraries WEL and MEL see the applicable operations without reference to a particular graphical platform.Classes “Object-oriented re- such as PLATFORM WINDOW provide the link to a graphical platform such as Windows,OS/2-Presentation-Manager or Unix-Motif,they give access to the platform-page44 specific mechanisms(encapsulated through a library such as WEL or MEL). A class such as WINDOW will then combine its two parents through features which effect (implement)the deferred features of GENERAL WINDOW by using the implementation mechanisms provided by PLATFORM WINDOW. PLATFORM WINDOW (like all other similar classes)needs several variants,one On the notion ofAce for each platform.These identically named classes will be stored in different directories; see“Assembling a the Ace for a compilation (the control file)will select the appropriate one. system".page 198 This solution works,but it has the drawback of tying the notion of WINDOW closely to the chosen platform.To transpose an earlier comment about inheritance:once a Motif window,always a Motif window.This may not be too bad,as it is hard to imagine a Unix window which,suddenly seized by middle-age anxiety,decides to become an OS/2 window.The picture becomes less absurd if we expand our definition of"platform"to include formats such as Postscript or HTML;then a graphical object could change representation for purposes of printing or inclusion in a Web document. The observation that we might need a looser connection between GUI objects such as a window and the underlying toolkit suggests trying the client relation.An inheritance link will remain,between WINDOW and GENERAL WINDOW;but the platform dependency will be represented by a client link to a class TOOLK/T representing the underlying "toolkit"(graphical platform).The figure at the top of the facing page illustrates the resulting structure,involving both client and inheritance. An interesting aspect of this solution is that it recognizes the notion of toolkit as a full- fledged abstraction,represented by a deferred class TOOLK/T.Each specific toolkit is then represented by an effective descendant of TOOLKIT such as MOTIF or MS WINDOWS. Here is how it works.Each class describing graphical objects,such as WINDOW,has an attribute providing access to the underlying platform: handle:TOOLKIT
818 USING INHERITANCE PROPERLY §24.3 Class GENERAL_WINDOW and similar ones such as GENERAL_BUTTON are deferred: they express all that can be said about the corresponding graphical objects and the applicable operations without reference to a particular graphical platform. Classes such as PLATFORM_WINDOW provide the link to a graphical platform such as Windows, OS/2-Presentation-Manager or Unix-Motif; they give access to the platformspecific mechanisms (encapsulated through a library such as WEL or MEL). A class such as WINDOW will then combine its two parents through features which effect (implement) the deferred features of GENERAL_WINDOW by using the implementation mechanisms provided by PLATFORM_WINDOW. PLATFORM_WINDOW (like all other similar classes) needs several variants, one for each platform. These identically named classes will be stored in different directories; the Ace for a compilation (the control file) will select the appropriate one. This solution works, but it has the drawback of tying the notion of WINDOW closely to the chosen platform. To transpose an earlier comment about inheritance: once a Motif window, always a Motif window. This may not be too bad, as it is hard to imagine a Unix window which, suddenly seized by middle-age anxiety, decides to become an OS/2 window. The picture becomes less absurd if we expand our definition of “platform” to include formats such as Postscript or HTML; then a graphical object could change representation for purposes of printing or inclusion in a Web document. The observation that we might need a looser connection between GUI objects such as a window and the underlying toolkit suggests trying the client relation. An inheritance link will remain, between WINDOW and GENERAL_WINDOW; but the platform dependency will be represented by a client link to a class TOOLKIT representing the underlying “toolkit” (graphical platform). The figure at the top of the facing page illustrates the resulting structure, involving both client and inheritance. An interesting aspect of this solution is that it recognizes the notion of toolkit as a fullfledged abstraction, represented by a deferred class TOOLKIT. Each specific toolkit is then represented by an effective descendant of TOOLKIT such as MOTIF or MS_WINDOWS. Here is how it works. Each class describing graphical objects, such as WINDOW, has an attribute providing access to the underlying platform: handle: TOOLKIT WINDOW PLATFORM_ WINDOW ∗ GENERAL_ WINDOW On the platformspecific libraries WEL and MEL see “Object-oriented rearchitecturing”, page 441 Platform adaptation through inheritance On the notion of Ace see “Assembling a system”, page 198