la poo et le secret de l'héritage -- julien

Upload: paul

Post on 09-Jan-2016

14 views

Category:

Documents


0 download

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…

TRANSCRIPT

  • La POO et le secret

    de lhritage

    Julien E. Harbulot | [email protected]

    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)