Trait ou héritage, il faut choisir
C’est avec la version 5.4.0 que les Traits sont apparus en PHP. Il s’agit d’un mécanisme de réutilisation de code, introduit dans le but de réduire certaines limites de l’héritage simple. Je constate que de plus en plus de développeurs se mettent à utiliser les Traits à tel point que ces certains n’utilise même plus l’héritage et préfère avoir recours à ce mécanisme. Que se soit avant de faire un héritage ou avant d’utiliser un trait, il convient de se poser la question de quand utiliser l’un ou l’autre ?
Revenons rapidement sur ce qu’est un héritage. L’héritage permet d’établir une relation (de type parent/enfant) entre des classes. Lorsque l’on étend une classe, la classe fille hérite alors de toutes les méthodes publiques et protégées de la classe parente. Il est également possible que la classe fille redéfinisse certaines de ces méthodes pour les adapter à ces spécificités.
Les traits quand à eux ont comme seul objectif de partager du code entre classes sans structuration hiérarchique. Il n’y a donc ici pas de relation entre les classes qui utilisent les traits. Il partage seulement un comportement.
Il faut bien avoir cette distinction lorsque vous écrivez du code. Voici par exemple, du code que j’ai pu voir récemment sur un projet :
Il faut faire attention à l’utilisation des traits car ces derniers peuvent
facilement casser les aspects classiques de la programmation objet. Dans ce bout
de code, les classes UserRepository
et GroupRepository
n’implémente pas d’interface
commune, elle utilise un trait. De ce fait, le principe de programmation SOLID peut
facilement être rompu.
Dans ce cas-là, un héritage semble plus adapté :
De plus en utilisant un héritage, il est plus facile de faire du typage explicite.
Il est alors possible de vérifier que la classe fournit en paramètre est bien de
type Repository
, ce qui n’est pas possible avec un trait. De plus il sera plus
facile de respecter le principe de substitution de Liskov.
Il est préférable d’utiliser les traits pour partager du code avec des classes qui n’ont pas de lien entre elles. Prenons comme exemple :
Dans l’exemple précédent, la classe Mailer
a pour but d’envoyer un mail à un
utilisateur et la classe Writer
de générer un affichage à l’écran. Ces deux
classes n’ont aucun rapport et pourtant elles ont toutes les deux besoins de
traiter du texte fourni en paramètre. L’héritage n’a ici pas sa place et il
convient donc de partager le code via l’utilisation d’un trait.
Un autre exemple concret de l’utilisation des traits est l’implémentation du
pattern Singleton.
Lorsque l’on souhaite utiliser ce dernier, le premier réflexe pourrait être de
créer une classe Singleton
et de faire hériter nos objets de cette dernière.
Mais ce n’est pas toujours possible à cause de l’impossibilité de faire de
l’héritage multiple. Partant du principe qu’il est préférable de favoriser un
héritage “fonctionnel”, il convient dans ce cas-là, d’avoir recours aux traits pour
résoudre le problème :
Héritage et Trait, les deux concepts ont leur place dans nos projets, utilisons-les à bon escient et au bon moment.