Greg's Devblog Par un développeur, pour les développeurs

20nov/100

Réutiliser du code sans héritage multiple

L'héritage multiple : pas sans problèmes

En C++, on a l'héritage multiple, qui permet de rapidement factoriser du code, mais peut poser certains problèmes de "collisions" si plusieurs classes dont on hérite implémente une méthode du même nom, ou si plusieurs classes dont on hérite héritent elle-mêmes d'une même classe. Exemple simple : je créé un PointColore, qui hérite de Point3d et de Color. Que se passe-t-il si Point3d hérite d'une classe "Triplet" (pour x, y, et z), et Color aussi (mais pour r, g, et b) ?

Et histoire de donner un exemple dans l'autre sens, on pourrait avoir une classe VoitureAmphibie qui hérite de Voiture et de Bateau, et ces 2 classes qui héritent de "Transport" (qui contient alors des coordonnées x, y, et z, par exemple). Dans ce cas, on ne veut pas avoir les données de Transport en double, mais juste en 1 exemplaire...

Bref, ils existent plein de petits ajouts à la syntaxe pour palier à ça, mais ça introduit quand même une certaine ambiguïté, et donc un risque d'erreur (tout comme la surcharge à outrance).

Sans héritage multiple : la galère

Pour les exemples ci-dessus, comment procède-t-on si on n'a pas d'héritage multiple ? C'est notamment le souci en Java, et pour résoudre cela les interfaces ont été introduites... Sauf que c'est super, ça marche bien, mais avec les interfaces on ne factorise pas du tout le code... Alors oui, on peut créer un objet de la classe dont on voudrait faire l'héritage multiple, avoir un champs de cette classe dans notre classe, et puis "binder" toutes les méthodes, ça reste un peu chiant, redondant, et lourd pour pas grand chose...

Et le PHP dans tout ça ?

Il existe probablement plein de méthodes pour palier à ce problème, et je ne les connais pas forcément. Mais en lisant un peu par hasard des informations sur la prochaine version de PHP (suite à l'abandon de PHP6 !), je suis tombé sur une solution que je trouve à la fois simple et élégante : les traits.

Un bout de code vaut mieux que de longs discours, et comme je n'ai ni le temps, ni l'envie d'en faire un moi-même, je vais tout simplement repomper celui du wiki d'où j'ai tiré ces informations...

trait ezcReflectionReturnInfo {
   function getReturnType() { /*1*/ }
   function getReturnDescription() { /*2*/ }
 }

 class ezcReflectionMethod extends ReflectionMethod {
   use ezcReflectionReturnInfo;
   /* ... */
 }

 class ezcReflectionFunction extends ReflectionFunction {
   use ezcReflectionReturnInfo;
   /* ... */
 }

C'est tout simple, on peut voir les traits un peu à l'image des classes abstraites : on peut y mettre du code, mais on ne peut pas les instancier. Et en fait, l'idée est juste que ça revient à inclure le contenu des traits directement dans la classe. Et ça permet d'intégrer du code venant de différents endroits, en gardant un héritage simple, et donc en évitant le surcoût et/ou certaines des emmerdes liées à l'héritage multiple.

Alors certes, on va me dire qu'en pratique, on a les mêmes soucis qu'avec l'héritage multiple si on inclus plusieurs traits qui ont des méthodes ayant un même nom... Les équipes derrière PHP ont pensé à tout !

 class Talker {
   use A, B {
     B::smallTalk instead A;
     A::bigTalk instead B;
     B::bigTalk as talk;
   }
 }

Je ne prends pas la peine de détailler les classes et tout car seul le bout de code ci-dessus est intéressant. Mais en gros, les traits A et B contiennent des méthodes smallTalk et bigTalk, et on a donc un petit conflit si on inclus les 2 sans rien préciser. Du coup on peut préciser quelles méthodes utiliser (la méthode smallTalk issue de B, au détriment de celle issue de A), et on peut aussi renommer une méthode (la méthode bigTalk de B) en un autre nom si on veut pouvoir l'appeler tout de même...

Au final, on obtient un système souple et flexible, qui me fait penser à un mélange entre héritage mutiple et système de macros pour inclure du code à la compilation. D'après le wiki, le système des traits existent déjà dans d'autres langages, dont Scala, donc j'y jetterai un coup d'oeil à l'occasion. Mais bon, j'étais content de trouver une solution viable à tous ces problèmes, autres que les non-solutions proposées en Java...

Commentaires (0) Trackbacks (0)

Aucun commentaire pour l'instant


Laisser un commentaire


Aucun trackbacks pour l'instant