Polymorphic Hierarchy Subimplementor Subimplementor methods are the key Bobby Woolf I've I've notic noticed ed that that at least least half half the the metho methods ds I write write are are not not very very origin original. al. Sure, Sure, lots lots of the the methods I write are real methods that do real work and have real names that are unique. Yet in the process of that real programming. I also write a ton of methods that don't require much thought. I implement lots of getter and setter methods, methods, and initializat initialization ion and instance creation methods, and I also write a fair numer of methods methods that suimplem suimplement ent methods methods alread already y defined in a superclass. I'm finding that it is these these suimp suimplem lement entor or metho methods ds that that are are the key to polymor polymorphism phism.. !sed aggressi aggressivel vely y and consistently, polymorphic methods lead to polymorphic classes and ultimately ultimately polymorphic hierarchies. I'll discuss what a polymorphic hierarchy chy is as well well as a patt patter ern n I call call "#em "#empl plat ate e $lass%. #he top class in a polymorphic hierarchy is a #emplate $lass. &!SI() *#+ S$&I#I(S /et's use printn0 as an e1ample. 2isual3orks implements printString in 4ect via printn0. Since 4ect55printn0 is generic, I frequently suimplement it int my classes to tell me not 4ust what class this instance come from, ut to also provide some clue as to which instance this is. 6eing a good corporate citizen, I comment my new new implem implemen entor tor of print printn0 n0 with with a descript scription ion to give give the ne1t ne1t progr programm ammer er some some clue as to what this method does. #he #he desc descri ript ptio ion n in 4e 4ect ct55 55pr prin int tn0 n0 says, "7ppend to the argument aStream a sequence quence of chara characte cters rs that that descr descrie iess the receiver.% I couldn't have said it etter myself. In fact, why should I try8 3hat do you think this method does8 +aving seen a hundred other implementors of printn0 throughout the system, all of which do the same thing, what do you think this one does8 *y philosophy is to duplicate as little effort as possile, so why should I comment a method that's already een commented elsewhere8 Insisted, I 4ust descrie my method y saying, "See superimplementor.% It's my way to let the method say, "/ook I do the same thing my superimplementor does. 3hat else would I do8 If you don't know what that is,
see the description in the superimplementor.% 7nothe 7notherr clue clue that that my implem implement entor or of printn0 does the same thing as 4ect's is that oth implementors appear in the same method protocol, printing. #he printing protocol in 4ect 4ect says, says, "#hese "#hese are are all of the printi printingng-typ type e thin things gs the the recei eceiv ver know knowss how how to do. do.% 6y putting my implementor in the printing protocol, I'm saying that my method is also used for prin printi ting ng.. #hat #hat's 's a good good thin thing, g, eca ecaus use e I'm I'm suimplementing a method that is already used for printing. (ot only should a suimplementor do the same thing as it's superimplementor, it should also appear in the same protocol. protocol. ( 9I(I() I*/*(#& 3hen 3hen I've I've writte written n all all the implem implement entors ors of a mess messag age, e, ther there e's only only one one desc descri ript ptio ion, n, and and that's in the method in the superclass that defines the hierarchy hierarchy.. #his implementor implementor is usually pretty lame0 it 4ust returns self, or the suclass&espo &esponsi nsiil ility ity error error,, or some some defau default lt impleimplementation that won't cause troule when suclasses inherit it. 6ut even though the implementat mentation ion is lame, lame, the the metho method d still still defin defines es what this message does for the whole hierarchy. #hus, any suimplementors had etter do the same thing0 their implementations are different, ent, ut their their purpo purposes ses are are the same. #hus, #hus, their descriptions are, "See superimplementor.% #he #he same same princ rincip iple le applie pliess to the the Smalltalk hait of having one method simply call another method, which has also the same name e1cept for an e1tra parameter. 9or e1ample, 4ect55changed sends changed0 then 4ect55changed0: sends changed0with0. #here is no need to descrie the purpose of all three of these methods. nce you know what change changed0w d0with ith00 does, does, you you know know that that change changed d and changed0 work in the same way, e1cept that they use defaults for some of the parameters. So my description for 4ect55changed would e "See "See change changed0. d0.%: %: "See "See chang changed0 ed0wit with0. h0.% descries 4ect55changed0. If I suimplemented any of these methods, the description would e "See superimplementor superimplem entor..%
7(7#*Y 9 7 *#+ S$&I#I( So what kinds of immodest oasts and secret confe confessi ssions ons end up in a metho method d descri descripti ption on anyway8 9irst of all, I try to avoid rephrasing the method name. !nfortunately, I've written many methods like product$ode, whose description says "&eturn the code for the receiver's product.% h, really8 #hese sorts of methods are usually 4ust returning an instance variale;s value, so when I can't think of anything else to say,
Used aggressively and consistently, polymorphic methods methods lead to polypolymorphic classes and ultimately polymorphic hierarchies. I descrie them with, ")etter.% itto for setter methods. )iven that you should already know what the instance variale does, there's really not much to say aout getter and setter methods. Second, I try to use method description to descrie the entire method. I generally don't like to comment individual lines of code. It's tempting to do so when I write a line or two of code that is so izarre that another programmer has no chance chance to under understa stand nd what what he's he's looking at. !nfortunately, I'm not going to e ale ale to cove coverr this this lun lunde derr y incl includ udin ing g an equally convoluted comment. Instead, I hide the izarre code in a new method with a descriptive name. #he comment I would have included to e1plain the code ecomes the method's description. !&S 7( I*/*(# I*/* (#7#I( 7#I( #7I/S #7I/S #hird, I split my method description into two parts0 parts0 purpo purpose se and implem implement entati ation on detail details. s. Purpose e1pl e1plai ains ns what what the the meth method od does does.. I phrase it as, "If you send this message to this o4ect, o4ect, here here's 's what' what'll ll happe happen. n. #his #his metho method d will...% 1amples might e, "Sort the receiver's elements.% "&ead the ne1t item and return it.% and "7nswer whether the receiver contains errors.% Implementation details is optional. #his is my litany of e1cuses for why the code is so strange. /uckily, I write reasonaly good code most of the time so only a few of my methods need these e1planations. e1planations. 7 method's purpose is reusale. 7ll of the implementors of a message within a hierar-
chy had etter have the same purpose. #his, I only document the message's purpose in the implementor at the top of hierarchy. ther implementors document their purpose with "See suimplementor.% 7 method's implementation details are not reusale. #wo implementors of a message should not have the same implementation details. If they do, they're duplicating code. #hus #hus,, you can can easi easilly spli splitt your our own own method descriptions into purpose and implementation details. #o do so, separate the commentary that e1plains what the method does from that written e1plains how. #he what com commentary is the method's purpose: it should e the same for every implementor in the hierarchy. #he how commentary commentary descries the implementation details, so it should e different
knows what to do, ut not how to do it. #he suclass implements the how. $onsider the $ollection hierarchy. 7 $ollection can accept requests to add and remove elements, iterate over them, and answer how many elements it has, ut it does not know how to fulfill many requests. #he how depends on the implementation: is the collection a list, a tree, a hash tale, or what8 Suclasses of $ollection implement the details of how. Set
9I(I() /Y*&+IS* #his definition of polymorphism is more e1tensive than the ones you usually hear. #he sound ite I learned was that two methods are polymorphic if their names are the same. #his is clearly not always true. #hink aout the messages value and value0. #hey have lots of implementors, ut are those implementors polymorphic8 If they are polymorphic, what do those messages do8 #hat depends on the receiver. In a 2alue*odel, value and value0 are accessor I() 7 +I&7&$+Y /Y*&+I$ #his rings me ack to my "See suimplementor% method comments. 7s I got used to commenting my methods this way, I got used to thinking of my methods polymorphically, which made me more careful to implement them polymorphic. 7s the methods in my hierarchies ecause cause more more polymor polymorphic phic,, the hierarch hierarchies ies ecame more polymorphic. 7s my hierarchies ecame came more more polymo polymorp rphic hic,, they they ecam ecame e more more
fle1ile, fle1ile, reusale reusale,, e1tensi e1tensile, le, easier easier to learn, learn, and all of that other good stuff. #he prolem comes when there is no superimplementor to see. Sometimes I'm implementing a method and I rememer that I've already implemented another method with the same name. #hey're doing the same thing, so they have the same purpose. If one of them is a suimp suimplem lement entor or of the the other other,, I descri descrie e the the suimplem suimplemento entorr with "See suimplem suimplemento entorr.% 6ut I can't do this when they are in peer classes and there is no superimplementor. 3hen this happens, the code is telling me, "You're missing a superimplementor.% I don't want to duplicate any effort I can avoid, which includes defining what this message is supposed to do in this hierarch erarchy y. So I introduc introduce e a superimpl superimplemen ementor tor,, document the purpose there, and give it a default implementation. (ow I can document the suimplem suimplemento entors rs with "See superimp superimpleme lemenntor.% f course, another prolem I sometimes run into is that there's no superclass to put the superi superimpl mpleme emento ntorr in. #wo #wo classe classess with with two two supposed supposedly ly polymor polymorphic phic implemen implementatio tations ns of the same message are peer classes that have no relat relation ionshi ship p to each each other other in the hiera hierarc rchy hy.. #heir #heir first first commo common n super supercla class ss is someth something ing gener generic ic like like 7ppli 7pplicat cation ion*o *odel del or 4ect 4ect.. I'm I'm definitely not going to add
Most of the hierarchies we know and love are polymorphic hierarchies. This is no coincidence. this domain-specific message to such a general class, as though all suclasses should support this message. So what can I do8 If these two implementors are really polymorphic, then their classes are proaly going to need to e polymorphic as well. If so, the most maintainale way to make two classes polymorphic is to put them them in the the same same hier hierar arch chy y and and make make the the whole hierarchy hierarchy polymorphic. Since this hierarchy does not e1ist, I need to create a new astract class that descries the polymorphic ehavior of this hierarchy, then suclass my two concrete classes off of it. #he hierarchy now e1ist, so now I can add the superimplementor to the super supercla class. ss. #he purpo purpose se of the messag message e goes in the superimplementor, and the superimplementors 4ust say "See superimplementor.%
#+ #*/7# # */7# $/7SS 7##&( 7##&( #he astract class I introduced to make the hierarchy polymorphic is what I call a "#emplate $lass.% #emplate $lass is a pattern that creates polymo polymorph rphic ic hiera hierarch rchies ies.. It is simila similarr to the the ? #emplate *ethod pattern. 3hereas a #emplate *eth *ethod od defi define ness the the inte interf rfac ace e for for a meth method od while deferring the details to suclasses, a #emplate $lass defines the interface for a class @a new type@ while deferring the implementation details to suclasses. #his leads to a whole hierarchy of classes that are polymorphic. 7 #emplate $lass is typically full of #emplate *ethods. #+ 27/!*/ +I&7&$+Y #he 2alue*odel hierarchy in 2isual3orks is a good e1ample of a polymorphic hierarchy. #he class 2alue*odel defines the hierarchy y saving that all instances will understand messages such as value, value, value0, and on$hangeSend0to0. on$hangeSend0to0. 7ll suclasses implement these messages according to how each class works. 7 class will inherit some some of the implem implement entati ation onss if they they alrea already dy work appropriately appropr iately.. 6ecause all 2alue*odel suclasses support this core interface, a collaorator collaorator can use a 2alue*o alue*odel del withou withoutt havin having g to worry worry aout aout what suclass the instance came from. 3hether the instance is a 2alue+older 2alue+older,, an 7spect7daptator, tor, or a #ype$o #ype$onve nverter rter,, it support supportss the core core value interface. #his is the enefit of polymorphism at the class level. 6ecause the hierarchy is polymorphic, you can use any class in the hierarchy as easily as any other and the collaorating ones
•
•
•
•
•
•
•
•
•
type types, s, the the same same side side eff effects ects,, and and the the same return type. #wo #wo meth method odss that that are are poly polymo morp rphi hicc should appear in the same method protocol. 7 metho method d descri descripti ption on docume documents nts two two things0 the purpose and the implementation details. 9or two two metho methods ds to e polymo polymorph rphic, ic, they they need need to have have the the same same purpo purpose. se. #hey should not have the same implementation details. 9or two classes to e polymorphic, they need to share the same core interface polymorphic polymorphic messages. 7 collaorator can use the two classes interchangealy if it only uses messages in their core interface. 9or a hierarchy to e polymorphic, all of its classe classess must must share share the the same same polypolymorphic core interface. #he polymorphic hierarchy and its core inte interf rfac ace e are are defi define ned d y an ast astra ract ct class. I call an astract class that fulfills this role a #emplate $lass. 7 polymor polymorphic phic hierarch hierarchy y encapsul encapsulates ates code that is highly reusale, fle1ile, e1tensile, and 4ust plain good .
#o learn more aout implementing your own polymorphic hierarchies, I suggest reading the paper "&eusaility #hrough Self-ncapsulation% Self-ncapsulation% B y >en 7uer. It is a pattern language that descries how to develop a class hierarchy that achieves reuse via inheritance, while maintaining each class's encapsulation. 7lthough polymorphism is not an e1plicit goal of the pattern language, hierarchies developed this way tend to e polymorphic ones. &9&($S 1. )amma. ., +elm, &., Cohnson, &., and 2lissides.= SI)( 7##&(S0 /*(#S 9 &!S76/ 6C$#&I(# S9#37&, S 9#37&, 7ddison-3esley, 7ddison-3esley, &eading, *7, ?DDE. http0AAwww.aw.comAcpA)amma.html B. $oplien, $oplien, Cames, Cames, and and Schmi Schmidt dt ., ., <ds. <ds.=, =, 7##&( /7()!7)S 9 &)&7* SI)(, 7ddison-3esley, &eading, *7, ?DDE. http0AAhegschool.aw.comAcsengAauthor sAcoplienApatternlangApatte sAcoplienApatt ernlangApatternlang.htm rnlang.htm 6oy 3oolf is a senior memer of technical staff at >nowledge Systems $orp. in $ary, (orth $ar $arolin olina. a. +e ment mento ors clie client ntss in the the use use of 2isual3 2isual3ork orks, s, (2Y, (2Y, and design design patterns. patterns. +e welcomes your comments at woolfFacm.org or at http0AAwww.ksccary.com