View
14
Download
0
Category
Preview:
DESCRIPTION
Depuis la popularisation du paradigme de programmation fonctionnel, la programmation orientée objet (POO) est de plus en plus critiquée. Ces critiques reposent toutefois sur une utilisation inappropriée de la POO, et surtout de l’héritage, dont le rôle semble mal compris.Je vous propose de faire le point sur l’héritage et sur son rôle vis-à-vis du polymorphisme. Nous verrons que la POO n’est pas morte, et qu’elle permet de réutiliser du code existant d’une façon inattendue…
Citation preview
La POO et le secret
de lhritage
Julien E. Harbulot | web@julienh.fr
Fvrier 2015
Depuis la popularisation du paradigme de programmation fonctionnel, la programmation oriente
objet (POO) est de plus en plus critique. Ces critiques reposent toutefois sur une utilisation
inapproprie de la POO, et surtout de l'hritage, dont le rle semble mal compris.
Cet article fait le point sur l'hritage et sur son rle vis--vis du polymorphisme. Nous verrons que
la POO n'est pas morte, et qu'elle permet de rutiliser du code existant d'une faon inattendue.
Sommaire
Quest-ce que lhritage ? 3
Une utilisation encore trop courante : lhritage dimplmentation 4
Cas pratique 4
Lhritage dimplmentation en chec 7
La vritable raison dtre de lhritage : lhritage dinterface 8
Cas pratique 8
Le polymorphisme 9
Lhritage dinterface et le systme de type 10
Lhritage dinterface tient ses promesses 11
Lhritage dinterface permet dinverser les dpendances 11
Lhritage, au-del des interfaces 13
Le principe de substitution de Liskov 13
Cas pratique 13
Lhritage nexprime pas la relation est un 14
Pour aller plus loin 14
La bonne mthode pour rutiliser des objets existants : la composition 15
Lhritage dimplmentation est source de rigidit 15
La composition permet dutiliser linversion de dpendance 15
Les mthodes de transfert et la composition 16
POO et rutilisation de code : en bref 17
La POO et le secret de lhritage | Julien E. Harbulot
Page 3 sur 17
Quest-ce que lhritage ?
Lhritage est un mcanisme de la programmation oriente objet (POO) qui permet de lier deux
classes, que lon appelle classe-mre et classe-fille. Un tel lien entre deux classes a les
consquences suivantes :
la classe-fille dispose de toutes les mthodes et de tous les attributs de porte public et
protected de la classe-mre, et peut les utiliser comme sil sagissait des siennes,
le type de la classe-fille peut tre utilis partout o celui de la classe-mre peut ltre,
certaines mthodes de la classe-mre peuvent tre r-crites dans la classe-fille. Ces
mthodes sont appeles mthodes virtuelles, et le mcanisme permettant une telle
modification sappelle le polymorphisme.
Lhritage permet de rutiliser du code existant, de limiter la duplication, et donc de simplifier la
maintenance du code.
Nous verrons nanmoins que les mthodes pour y parvenir ne sont pas les premires auxquelles
lon pense. En effet, il peut tre utilis de deux faons diffrentes :
pour hriter de limplmentation dune classe existante, et ainsi rutiliser son code. On parle
alors dhritage dimplmentation,
pour hriter de linterface dune classe existante dans le but de permettre la classe-fille dtre
substitue la classe-mre de faon polymorphique. Cette utilisation sappelle hritage
dinterface et rend possible la r-utilisation de code dune application qui manipule des
instances de la classe-mre en lui faisant manipuler des instances de la classe-fille, sans que
ce dernier nait besoin dtre modifi.
La POO et le secret de lhritage | Julien E. Harbulot
Page 4 sur 17
Une utilisation encore trop courante : lhritage dimplmentation
Lhritage est parfois utilis comme outil pour factoriser du code entre les objets. Lorsque plusieurs
objets utilisent le mme code, une solution possible pour viter la duplication est de placer ce code
dans une classe-mre commune : cest lhritage dimplmentation.
Cas pratique
Voici un exemple o lon a factoris le code commun aux classes Peugeot et Renault dans une
classe-mre appele Voiture.
class Voiture{ protected: Roue roue_avant_gauche; Roue roue_avant_droite; Roue roue_arriere_gauche; Roue roue_arriere_droite; public: void tourner_a_gauche() { roue_avant_gauche.pivoter_a_gauche(); roue_avant_droite.pivoter_a_droite(); } /* et autres mthodes similaires. . . */ void accelerer() { roue_avant_gauche.tourner_plus_vite(); roue_avant_droite.tourner_plus_vite(); }
La POO et le secret de lhritage | Julien E. Harbulot
Page 5 sur 17
/* et autres mthodes similaires. . . */ }; class Peugeot : public Voiture{ public: string nom_du_modele() { return Peugeot 204 ; } // etc. }; class Renault : public Voiture{ public: string version() { return Renault 12 Gordini ; } // etc. };
Dans lexemple ci-dessus, les classes Peugeot et Renault partagent du code qui a t factoris
dans la classe-mre Voiture, et il est possible de faire appel ce code de faon transparente : Peugeot ma_voiture; string mon_modele = ma_voiture.nom_du_modele(); ma_voiture.tourner_a_gauche(); // utilisation du code de la classe-mre ma_voiture.accelerer(); // utilisation du code de la classe-mre
Continuons notre exemple, et supposons dsormais que lon dcide damliorer notre application
en ajoutant de nouveaux modles : une Twingo propulsion (arrire), et une Alpine A110
propulsion (arrire) galement.
Comme nous sommes partis du principe que la classe Voiture tait traction (avant), il faut la
modifier pour extraire le code correspondant cet aspect et crer deux classes distinctes
(VoitureTraction et VoiturePropulsion) afin de factoriser le code responsable de la gestion
de lacclration.
class Voiture{ // Nous enlevons le code qui soccupe dacclrer. protected: Roue roue_avant_gauche; Roue roue_avant_droite; Roue roue_arriere_gauche; Roue roue_arriere_droite; public: void tourner_a_gauche() { roue_avant_gauche.pivoter_a_gauche(); roue_avant_droite.pivoter_a_droite(); } /* et autres mthodes similaires. . . */ }; class VoitureTraction : public Voiture { // lavant public: void accelerer() { roue_avant_gauche.tourner_plus_vite(); roue_avant_droite.tourner_plus_vite(); } /* et autres mthodes similaires. . . */ }; class VoitureTraction : public Voiture { // larrire public: void accelerer() { roue_arriere_gauche.tourner_plus_vite(); roue_arriere_droite.tourner_plus_vite(); } /* et autres mthodes similaires. . . */ }; class Peugeot : public VoitureTraction{ public: string nom_du_modele() { return Peugeot 204 ; } // etc. };
La POO et le secret de lhritage | Julien E. Harbulot
Page 6 sur 17
class Renault : public VoitureTraction{ public: string version() { return Renault 12 Gordini ; } // etc. }; class Twingo : public VoiturePropulsion{ // etc. }; class Alpine : public VoiturePropulsion{ // etc. };
Le code commun Twingo et Alpine est bien factoris dans la classe VoiturePropulsion,
tandis que le code partag par Peugeot et Renault est dans la classe VoitureTraction.
Remarquons que le changement apport la classe Voiture ncessite une re-compilation
de toutes ses classes filles.
Notre application fonctionne correctement, et nous dcidons maintenant de rajouter un modle
supplmentaire : Le 4x4 Citron C4 AirCross, qui est traction et propulsion
Traction et Propulsion ? Mais alors de quelle classe devra-t-il hriter ? VoitureTraction ou
VoiturePropulsion ? Les deux ?
- Si le 4x4 hrite des deux classes, alors il sera dot de 8 roues ! En effet, 4 sont dtenues par la
classe VoitureTraction, et 4 par la classe VoiturePropulsion.
Non seulement une telle situation gaspille de la mmoire car la moiti des roues sont superflues,
mais cest aussi une source de complexit inutile qui oblige jongler entre les attributs des deux
classes mres.
Cas particulier : si vous programmez en C++, vous savez peut-tre que ce langage offre la
possibilit dutiliser un hritage spcial entre la classe VoitureTraction et la classe Voiture,
que lon appelle lhritage virtuel. Nanmoins, lutilisation de lhritage virtuel est couteuse en
performance, et ne constitue pas une solution viable car cela diminuerait les performances de
toutes les classes qui hritent de VoitureTraction et pas seulement celles de notre classe 4x4.
- Plutt que de faire hriter notre 4x4 des deux classes VoitureTraction et VoiturePropulsion,
nous pourrions ne le faire hriter que de la classe VoitureTraction, et rcrire les mthodes
dacclration pour ajouter la gestion des roues arrires. Cependant, cette solution nest pas
satisfaisante car cela revient dupliquer le code de la classe VoiturePropulsion la main, or
cest justement ce que nous voulons viter !
Dans tous les cas, nous sommes bloqus : lhritage dimplmentation ne remplit pas ses
promesses et la duplication de code semble invitable.
La POO et le secret de lhritage | Julien E. Harbulot
Page 7 sur 17
Lhritage dimplmentation en chec
On voit ainsi que lhritage dimplmentation est maladroitement utilis dans le but de :
rutiliser du code existant dans de nouvelles classes,
factoriser du code commun plusieurs classes dans une classe-mre commune
et que son utilisation entraine les inconvnients suivants :
un couplage fort entre les deux classes qui oblige la classe-fille (et donc toute lapplication
qui en dpend) recompiler chaque changement de la classe-mre,
des hirarchies complexes et impossibles maintenir qui rendent la duplication de code
invitable.
Cest justement pour limiter lutilisation de lhritage dimplmentation que ladage prfrer la
composition lhritage sest rpandu ; et nous verrons prochainement que la composition,
lorsquelle est utilise de pair avec le polymorphisme, permet de rutiliser le code dobjets
existants de faon satisfaisante.
En clair, nombreux sont ceux qui voient dans lhritage un moyen de rutiliser
des objets existants en spargnant davoir crire des mthodes de
transfert[1]. Mais cest oublier que lhritage a pour unique vocation de
permettre la rutilisation du code client via le polymorphisme, dont il est le
vassal, comme nous allons le voir.
Il est donc bien question dune mauvaise comprhension du rle de lhritage,
dont lutilisation dnature va contresens des objectifs de la POO et produit
du code rigide, qui entraine :
un couplage fort, inutile entre deux classes,
des hirarchies trop complexes,
et terme : de la duplication de code. [1] voir chapitre sur la composition
La POO et le secret de lhritage | Julien E. Harbulot
Page 8 sur 17
La vritable raison dtre de lhritage : lhritage dinterface
Comme nous lavons vu dans la partie prcdente, lhritage ne permet pas de rutiliser des objets
existants. Il a en fait t conu pour accompagner le polymorphisme dans le but de permettre la
rutilisation dun autre type de code : le code dapplication, cest dire le code qui utilise nos
objets.
Cas pratique
Afin dillustrer lintrt du polymorphisme et de lhritage dinterface, voici un exemple o nous
sommes chargs de lcriture dune fonction crypter_fichier capable dencrypter un fichier,
selon un algorithme accessible depuis la fonction crypter_string : string crypter_string(string input); //fonction fournie void crypter_fichier(string adresse_du_fichier){ FILE fichier_source = ouvrir_fichier( adresse_du_fichier ); string contenu = lire_fichier( fichier_source ); string nouveau_contenu = crypter_string( contenu ); effacer_fichier( fichier_source ) ecrire_dans_fichier( fichier_source, nouveau_contenu ); }
La POO et le secret de lhritage | Julien E. Harbulot
Page 9 sur 17
Quelques jours plus tard, nous sommes chargs dcrire une nouvelle fonction capable de crypter des fichiers situs sur le rseau : void crypter_fichier_web(string adresse_web){ WEBFILE fichier_source = obtenir_fichier_distant( adresse_web ); string contenu = lire_fichier_web( fichier_source ); string nouveau_contenu = crypter_string( contenu ); WEBFILE nouveau_fichier = creer_avec_contenu( nouveau_contenu ); ecraser_fichier_distant( adresse_web , nouveau_fichier ); }
Remarquons que lalgorithme de cette seconde fonction est en tout point similaire celui de la
premire : ouvrir le fichier source ; calculer le contenu crypt ; remplacer le fichier source.
Malheureusement, cause de dtails lis la nature des fichiers, il nous est impossible de
rutiliser notre fonction initiale.
Condamns rcrire la mme fonction encore et encore ?
Heureusement non, grce au polymorphisme, qui permet de rutiliser la mme fonction dans les
deux cas
Le polymorphisme
Le polymorphisme permet de manipuler deux objets qui se comportent de la mme faon sans
avoir les distinguer. Par exemple, nous aimerions pouvoir crire une application capable de
crypter tout type de fichier, en faisant abstraction des dtails ncessaires leur manipulation.
Or, afin dtre en mesure de manipuler deux objets diffrents de la mme faon, il est ncessaire
que ces deux objets comprennent, et soit capables dexcuter, les mmes instructions. Cela veut
dire que ces objets doivent mettre disposition les mmes mthodes dans leurs interfaces
respectives.
Par exemple, les objets WebFile et DiskFile pourraient proposer les mthodes suivantes :
ouvrir( adresse )
obtenir_contenu()
ecraser_contenu_avec( nouveau_contenu )
class WebFile{ public: WebFile(string adresse); string obtenir_contenu(); void ecraser_contenu_avec( string nouveau_contenu ); }; class DiskFile{ public: DiskFile(string adresse); string obtenir_contenu(); void ecraser_contenu_avec( string nouveau_contenu ); };
Il serait alors trs simple de rutiliser le mme code dapplication pour manipuler indiffremment
ces deux objets :
void crypter_fichier( UnType? fichier_source ){ string contenu = fichier_source.obtenir_contenu(); string nouveau_contenu = crypter_string( contenu ); fichier_source.ecraser_contenu_avec( nouveau_contenu ); }
La POO et le secret de lhritage | Julien E. Harbulot
Page 10 sur 17
Cependant, un problme se pose pour les utilisateurs de langages statiquement typs tels C++ et
Java : quel type la fonction crypter_fichier doit-elle accepter en argument ? Si la fonction
demande un argument de type WebFile, il sera impossible de lutiliser avec des instances de
DiskFile, et rciproquement.
La solution rside dans lutilisation de lhritage dinterface, qui dfinit un contrat, pass par un
objet vis--vis de ses utilisateurs.
Lhritage dinterface et le systme de type
Lhritage dinterface est utilis dans les langages statiquement typs (C++, Java, etc.) pour
satisfaire aux contraintes imposes par le systme de type. Il nest dailleurs a priori ncessaire
que dans ces systmes.
Il permet en effet diffrentes classes de dclarer quelles remplissent un mme contrat vis--vis
de leurs utilisateurs, et donc quelles peuvent tre interchanges.
Lhritage dinterface met en scne deux acteurs :
une classe-mre responsable de dfinir le contrat, on lappelle Interface,
et une classe-fille qui sengage respecter le contrat nonc.
Dans notre tude de cas, lhritage dinterface est la clef qui permet de rutiliser la fonction
crypter_fichier avec diffrents types de fichiers :
class File{ // Dclaration de lInterface public: virtual string obtenir_contenu() = 0; virtual void ecraser_contenu_avec( string nouveau_contenu ) = 0; }; class DiskFile : public File{ /* Implmentation spcifique pour les fichiers locaux */ }; class WebFile : public File{ /* Implmentation spcifique pour les fichiers web */ }; void crypter_fichier( File& fichier_source ){ string contenu = fichier_source.obtenir_contenu(); string nouveau_contenu = crypter_string( contenu ); fichier_source.ecraser_contenu_avec( nouveau_contenu ); }
Remarquons quil est trs simple de faire voluer ce code pour prendre en compte les fichiers
situs sur un DVD, puisquil suffit dajouter une classe DVDFile, que lon pourra utiliser avec la
fonction crypter_fichier sans avoir besoin de la modifier.
Remarquons de plus que la relation A hrite de B devrait tre lue comme A spcialise B ,
ou encore comme A injecte son code dans B .
En effet, on peut considrer lInterface File comme une classe :
qui na pas dimplmentation au moment de la compilation,
et qui utilise le code dune de ses classes filles (DiskFile, WebFile, etc.) au moment de
lexcution.
La POO et le secret de lhritage | Julien E. Harbulot
Page 11 sur 17
Lhritage dinterface tient ses promesses
La rutilisation de code est ainsi rendue possible par la POO, qui propose des mcanismes
permettant du code existant de manipuler de faon gnrique diffrents objets, travers une
mme interface.
Les avantages dune telle approche sont multiples, car non seulement la POO limite la
duplication de code comme nous lavons vu avec lexemple de la fonction crypter_fichier
mais elle rend aussi possible lextension des fonctionnalits du code existant en lui
permettant de manipuler de faon transparente de nouvelles classes (telles DVDFile).
Ces mcanismes sutilisent de pair pour rpondre diffrentes contraintes :
le polymorphisme permet lutilisation gnrique de diffrents objets, et donc la
rutilisation dun mme code dapplication avec ces objets ;
en parallle, lhritage dinterface rend possible la mise en oeuvre du polymorphisme
dans les langages statiquement typs. Son utilisation permet dinformer le compilateur quune
classe respecte une Interface donne, et donc quelle peut tre utilise de faon
polymorphique.
En rsulte un code qui est la fois :
capable de bnficier des vrifications apportes par le systme de type ;
ouvert lextension, et ferm la modification.
Lhritage dinterface permet dinverser les dpendances
Si la POO rend possible lcriture dun code souple, cest parce quelle permet dinverser les
dpendances de compilation et dexcution ; cest dire que que le code qui manipule un objet
peut tre compil indpendamment de cet objet.
En dautres termes, la POO permet de dissocier :
le fait que la fonction crypter_fichier dpend des classes quelle manipule lors de
lexcution (par exemple, elle peut manipuler la classe DVDFile),
et le fait quelle puisse tre compile avant mme que ces classes ne soient crites (par
exemple, la classe DVDFile peut avoir t crite et compile aprs la fonction
crypter_fichier) !
Les schmas suivants illustrent linversion de dpendance rendue possible par lutilisation
conjointe de lhritage et du polymorphisme :
Schma 1 : Sans polymorphisme, lapplication (A) dpend de lobjet (B) quelle utilise.
En consquence, toute modification apporte (B) oblige une re-compilation de (A).
La POO et le secret de lhritage | Julien E. Harbulot
Page 12 sur 17
Schma 2 : Grce au polymorphisme, lapplication (A) ne dpend plus de lobjet (B) quelle utilise, mais seulement
dune Interface (I). Limplmentation de (B) peut changer sans que (A) nait besoin dtre re-compile.
Remarquons que linversion de dpendance na pas pour seul avantage de permettre lextension
du code existant. Cette dernire permet aussi de modifier limplmentation des objets
existants sans que cela nimpose une re-compilation du code qui les utilise.
En effet, tant donn que le code dapplication ne dpend que dune Interface, libre au
programmeur de modifier en secret la partie prive de ces objets dans laquelle rsident les dtails
de leur implmentation. Cette dichotomie entre interface publique dune part et implmentation
prive dautre part sappelle lencapsulation et constitue le troisime fer de lance de la POO.
En clair, la POO repose sur les trois notions fondamentales suivantes :
lhritage dinterface,
le polymorphisme,
et lencapsulation,
qui, lorsquelles sont utilises conjointement, rendent possible lcriture de code :
facilement rutilisable,
ouvert lextension, mais ferm la modification.
Lhritage est donc bien le vassal du polymorphisme, dont il rend lutilisation possible au sein des
langages statiquement typs.
La relation A hrite de B permet non seulement dinformer le compilateur que A prsente la
mme interface que B, mais plus encore, il sagit dun contrat pass par la classe-fille A envers ses
utilisateurs.
Ce contrat tablit un principe de substituabilit qui dpasse la simple dclaration dinterface
; cest pourquoi, contrairement la croyance populaire, la relation A est un B nest pas
synonyme de A hrite de B .
La POO et le secret de lhritage | Julien E. Harbulot
Page 13 sur 17
Lhritage, au-del des interfaces
Le principe de substitution de Liskov
What is wanted here is something like the following substitution property: If for each object o1 of
type S there is an object o2 of type T such that for all programs P defined in terms of T, the
behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
Barbara Liskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (1988).
Lhritage est un contrat bien plus fort quun simple engagement dfinir une interface publique.
Il indique en effet quune classe (la classe-fille) peut tre substitue une autre (la classe-mre).
Par consquent, la classe-fille doit non seulement proposer une implmentation des mthodes
dfinies par lInterface, mais ces mthodes doivent aussi se comporter de faon similaire ce qui
est attendu, cest dire respecter les mmes pr- et post-conditions1 .
Lexemple classique dillustration du principe de substitution de Liskov est celui du carr et du
rectangle.
Cas pratique
Supposons que lon dispose dune Interface dfinissant le comportement dun rectangle :
class Rectangle{ public: virtual void remplacer_largeur_par (unsigned int nouvelle_largeur ) = 0; virtual void remplacer_longueur_par(unsigned int nouvelle_longueur) = 0; /* et autres mthodes . . .*/ };
Comme le stipulent les principes de responsabilit unique (SRP) et de moindre surprise (PoLA), la
mthode remplacer_largeur_par() na pour seule fonction que de modifier la largeur du
rectangle.
Il serait dailleurs absurde et totalement inattendu que cette mthode modifie la longueur du
rectangle, ou tout autre attribut du rectangle.
Supposons de plus que nous ayons notre disposition une application capable de manipuler des
rectangles : unsigned int calculer_laire_de ( Rectangle& mon_rectangle ); void dessiner_sur_lecran ( Rectangle& mon_rectangle ); /* et autres fonctions . . .*/
Afin de pouvoir rutiliser cette application avec notre classe Carr, nous pourrions tre tents de
faire hriter la classe Carr de la classe Rectangle.
Nanmoins, il faudrait alors que la classe Carr fournisse une implmentation des mthodes
remplacer_largeur_par() et remplacer_longueur_par().
1 voir rubrique Pour aller plus loin
La POO et le secret de lhritage | Julien E. Harbulot
Page 14 sur 17
Or, comme par dfinition un carr est un rectangle dont la largeur vaut la longueur, cela implique
que la mthode remplacer_largeur_par() devrait mettre jour non seulement la largeur, mais
aussi la longueur du carr ! Alors mme que cette mthode nest pas cense modifier un
autre attribut que la largeur
Un tel comportement ne respecte pas le principe de substitution et sera souvent source de bugs
insidieux dans lapplication. Par consquent, la classe Carr ne doit pas hriter de la classe
Rectangle, et ce en dpit de lassertion vraie qui dit que : un carr est un rectangle .
Lhritage nexprime pas la relation est un
Nous avons vu dans le cas pratique ci-dessus que lhritage ne doit pas tre utilis pour
exprimer la relation est un . Par exemple, un carr est un rectangle, mais la classe Carr ne
doit pas hriter de la classe Rectangle.
Cela sexplique par le fait que gnralement, les reprsentants nentretiennent pas les mmes
rapports que les objets quils reprsentent.
Par exemple, deux personnes qui engagent une procdure de divorce seront chacune reprsente
par un avocat. Ces deux personnes sont maries, mais il est peu probable que leurs reprsentants
cest dire les avocats le soient aussi.
De la mme faon, les classes peuvent parfois reprsenter des objets du monde rel, mais il est
peu probable quelles entretiennent les mmes rapports que ces derniers.
Pour aller plus loin
En rsum, lhritage rend possible lutilisation polymorphique de diffrents objets en dfinissant
un contrat pass par ces derniers vis--vis de leurs utilisateurs. Non seulement ce contrat dfinit
explicitement une liste de mthodes publiques que ces objets doivent implmenter, mais il
implique aussi le respect implicite des pr- et post-conditions lies ces mthodes afin de garantir
que ces objets puissent tre substitus les uns aux autres.
Au sujet des pr-/post-conditions, lon pourra consulter larticle Wikipdia suivant :
[1] Programmation par contrat - http://fr.wikipedia.org/wiki/Programmation_par_contrat
Et pour approfondir la notion de substituabilit, lon pourra se rapporter larticle suivant :
Liskov substitution principle - http://www.objectmentor.com/resources/articles/lsp.pdf
Rappelons de plus que lhritage nest conu ni pour modliser la relation est un , ni pour
permettre la rutilisation du code dobjets existants.
Mais alors comment rutiliser correctement le code des objets existants ?
La POO et le secret de lhritage | Julien E. Harbulot
Page 15 sur 17
La bonne mthode pour rutiliser des objets existants : la composition
Lhritage dimplmentation est source de rigidit
Nous avons dj vu que lhritage tait souvent utilis tort pour factoriser du code commun
plusieurs objets, ou pour rutiliser le code dobjets existants.
Lutilisation de lhritage dimplmentation provoque la mise en place de hirarchies complexes.
Elle induit en outre un couplage fort entre la classe-mre et la classe-fille, alors que ce couplage
pourrait tre vit. En rsulte un code difficile changer et des re-compilations superflues.
Le couplage fort qui est induit par lhritage sexplique par le fait que lorsquune classe-fille hrite
dune classe-mre, elle est dote de tout le code de la classe mre. Par consquent, tout
changement apport au code de la classe-mre entraine un changement implicite du code de la
classe-fille. Cest dire que lorsque la classe-mre change, il faut re-compiler la classe-fille pour
prendre en compte ces changements.
Ce couplage fort est contre-sens des objectifs de la POO, qui a pour but de favoriser la
rutilisation de code et de limiter la propagation des changements. Cest pourquoi lhritage
dimplmentation ne peut pas tre considr comme une pratique oriente-objet.
La rutilisation dobjets existants reste cependant possible en POO grce la composition, qui
exprime au mieux cette intention. En effet, quest-ce que la composition, sinon le fait de fournir un
objet M un objet F dans le but den permettre la (r-)utilisation ?
La composition permet dutiliser linversion de dpendance
Dune part, la composition modlise au mieux les intentions du programmeur qui souhaite rutiliser
un objet existant ; dautre part, elle peut aussi tre utilise de pair avec le polymorphisme, dans le
but de limiter la propagation des changements.
En effet, nous avons vu que lhritage dinterface permettait de dcoupler deux objets via
lintroduction dune Interface.
La POO et le secret de lhritage | Julien E. Harbulot
Page 16 sur 17
Pour rappel, voici une illustration de ce phnomne que lon nomme inversion de dpendance :
Schma 1 :
Sans polymorphisme, lobjet (A) dpend de lobjet (B) quil
utilise.
Schma 2 :
Grce au polymorphisme, lobjet (A) ne dpend plus de
lobjet (B) quil utilise, mais seulement dune Interface (I).
La composition permet ainsi de rutiliser des objets existants sans introduire de couplage fort,
lorsquelle est utilise conjointement avec le polymorphisme.
Les mthodes de transfert et la composition
Certains programmeurs prfrent utiliser lhritage dimplmentation plutt que la composition car
cette dernire ne permet pas limport automatique des mthodes publiques de lobjet utilis dans
linterface publique de lobjet qui lutilise. Dans ltat actuel de nos langages de programmation,
son utilisation requiert en effet lcriture de mthodes de transfert.
Voici un exemple de mthode de transfert :
class ObjetUtilise{ public: virtual void methode_interessante(Type argument1, AutreType argument2) = 0; }; class ObjetUtilisateur{ public: ObjetUtilisateur(ObjetUtilise& outil); void methode_interessante(Type argument1, AutreType argument2){ //
La POO et le secret de lhritage | Julien E. Harbulot
Page 17 sur 17
Pour plus dinformation ce sujet, lon consultera utilement ladresse suivante :
https://bitbucket.org/Gauss_/method-forwarding-using-compositon-instead-of-inheritance
En conclusion, la composition est la meilleure faon du rutiliser des objets
car elle :
renseigne immdiatement sur les intentions du programmeur,
ninduit pas de couplage fort,
favorise un design souple et maintenable.
Son utilisation va dans le sens des objectifs de la POO et ne constitue en rien
un aveu de faiblesse de cette dernire. Au contraire, lutilisation de la
composition est une bonne pratique de programmation oriente-objet qui
favorise lutilisation du polymorphisme.
POO et rutilisation de code : en bref
Rutilisation des objets existants
mauvaise mthode : hritage dimplmentation
bonne mthode : composition + mthode de transfert (forwarding)
Rutilisation du code client (celui qui utilise des objets)
bonne mthode : hritage dinterface (donc polymorphisme)
Recommended