Le design pattern décorateur

Modifier les fonctionnalités d'un objet sans modifier sa classe ni l'hériter.

Le design pattern décorateur (decorator en anglais) permet de modifier / surcharger / améliorer les fonctionnalités d'un objet sans devoir en modifier la classe ou en hériter. Le décorateur agissant en tant que wrapper (objet qui en enveloppe un autre) sur l'objet dont il doit étendre les fonctionnalités, il doit hériter du même parent que cet objet.

Prenons l'exemple d'un texte en HTML que l'on voudrait mettre en gras, en italique ou colorié en rouge :

Nous avons une classe Texte, que nous ne souhaitons / pouvons pas modifier, avec une méthode getCode qui retourne le code HTML généré. Nous devons modifier le comportement de la méthode getCode et, pour préserver le polymorphisme, nous voulons que la nouvelle méthode se nomme aussi getCode. Au lieu de créer toutes les variantes de classes possibles (TexteEnRouge, TexteEnRougeEtGras, TexteEnGrasEtRouge etc...), nous allons externaliser les actions de modification du texte dans des classes qui ne ré-implémenteront pas toute la logique de getCode, juste la transformation.

Le pattern decorator en action

/* Classe mère Html dont hériteront les objets à décorer ainsi que les décorateurs */ abstract class Html { abstract public function getCode(); } /* Une classe à décorer */ class Texte extends Html { protected $code = ''; public function __construct($code) { $this->code = $code; } public function getCode() { return $this->code; } } /* Classe mère des décorateurs, prend un objet de type Texte en entrée */ abstract class DecorateurDeTexte extends Html { protected $texte = null; public function __construct(Html $texte) { $this->texte = $texte; } } /* Décorateurs de transformation du texte : * Ils ajoutent un comportement sur la méthode déjà existante getCode. */ class TexteEnGras extends DecorateurDeTexte { public function getCode() { return '<b>' . $this->texte->getCode() . '</b>'; } } class TexteEnItalique extends DecorateurDeTexte { public function getCode() { return '<i>' . $this->texte->getCode() . '</i>'; } } class TexteEnRouge extends DecorateurDeTexte { public function getCode() { return '<span style="color:red;">' . $this->texte->getCode() . '</span>'; } } $texteSansDecoration = new Texte('tutoriel sur le design pattern decorator'); $texteEnGras = new TexteEnGras($texteSansDecoration); echo $texteEnGras->getCode(); /* Sortie : <b>tutoriel sur le design pattern decorator</b> */ $texteEnGrasRougeItalique = new TexteEnItalique(new TexteEnRouge($texteEnGras)); echo $texteEnGrasRougeItalique->getCode(); /* Sortie : * <i><span style="color:red;"><b>tutoriel sur le design pattern decorator</b></span></i> */