Design patterns de structure
Alexandre Brabant*
2024
1 Le design pattern Composite
Le design pattern structurel Composite vous permet d'organiser vos objets sous forme d'arbre,
et ensuite de pouvoir travailler avec chacun des éléments de cet arbre comme s'il était un objet
indépendant.
Utiliser ce design pattern n'a de sens que si votre problème nécessite d'être modélisé sous forme
d'arbre. Par exemple, imaginez une application permettant de représenter la hiérarchie de la Ré-
publique française, et notamment de son gouvernement :
Gouvernement français (représentation vulgarisée)
Vous imaginez la complexité de représenter cela, par exemple pour obtenir les réponses aux ques-
tions :
Quels sont les supérieurs et/ou subordonnés d'un membre du gouvernement ?
* Adaptation d'un cours de Mickaël Andrieu (mis à jour le 16/02/2024)
Quel est le budget d'un ministère en y rattachant l'ensemble des salaires de tous les membres
le constituant ?
Le design pattern Composite suggère de faire implémenter une, voire plusieurs des interfaces com-
munes entre ces diérents objets :
Le design pattern Composite
1. L'interface ComponentInterface décrit l'ensemble des opérations qui sont communes à
tous les éléments de l'arbre.
2. On appelle Leaf (feuille) un élément qui n'a aucun "enfant" (ou sous-élément).
3. On appelle Composite (noeud) un élément qui a des enfants : le rôle d'un composite est
de déléguer les opérations à eectuer à ses enfants, jusqu'à parvenir aux Leafs qui
font les opérations eectives.
4. Le Client peut aller chercher n'importe quel élément de l'arbre en le manipulant au travers
de l'interface choisie.
Implémentons ce design pattern en PHP pour être capable de dénir le budget du gouvernement,
en restreignant le budget à l'ensemble des salaires des collaborateurs.
Tout d'abord, implémentons l'interface avec les fonctions requises et la fonction qui permet de
retrouver le budget.
Page 2
<?php
interface Employee
{
public function getBudget();
}
Le président de la République est un Composite (ou un employeur, si vous préférez).
Nous pouvons créer une interface pour cela, même si elle n'est pas strictement obligatoire dans
l'implémentation du design pattern :
<?php
interface Employer
{
public function getEmployees();
public function addEmployee(Employee $employee);
public function removeEmployee(Employee $employee);
}
// et une implémentation abstraite
abstract class SimpleEmployer
{
protected $employees = [];
public function getEmployees()
{
return $this->employees;
}
public function addEmployee(Employee $employee)
{
//
}
public function removeEmployee(Employee $employee)
{
//
}
}
Implémentons notre président !
Page 3
<?php
final class President extends SimpleEmployer implements Employee
{
const PRESIDENT_SALARY = 80000;
public function __construct()
{
$this->employees = [new PrimeMinister()];
}
public function getBudget()
{
$salary = self::PRESIDENT_SALARY;
foreach ($this->getEmployees() as $employee) {
$salary += $employee->getSalary();
}
return $salary;
}
}
En demandant le budget d'un ministère, le système va simplement redescendre dans la hiérarchie
en additionnant tous les salaires jusqu'au simple subordonné :
<?php
final class Teacher implements Employee
{
const TEACHER_SALARY = 30000;
public function getSalary()
{
return self::TEACHER_SALARY;
}
}
Enn, voici comment on pourrait "théoriquement" calculer le budget du ministère de l'Éducation :
<?php
// classe à créer
$eduMinister = new EducationMinister();
$eduBudget = $eduMinister->getBudget();
Page 4
Le composant Form du framework Symfony utilise le design pattern Composite.
2 Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter dynamiquement de nouveaux compor-
tements à un objet sans en modier le code.
Imaginez un site de vente privée : à chaque nouvelle vente, le système envoie un e-mail à tous les
utilisateurs inscrits, les invitant à se connecter et à proter de l'ore.
Quand le client décide de créer son application mobile, il vous demande de pouvoir laisser le choix
aux utilisateurs :
soit de recevoir des e-mails ;
soit de recevoir un SMS ;
soit les deux ;
soit... rien du tout.
Si nous modélisons cela sans rééchir, nous nirons avec un nombre de classes enfants qui corres-
pondront à chacune des possibilités (rien, "SMS + e-mail", "e-mail" et "SMS"), et si nous décidons
d'ajouter des notications push dans l'application mobile, le nombre de classes augmentera encore !
On peut penser que l'héritage de classe est la meilleure solution quand on veut altérer le compor-
tement d'un objet, mais l'héritage de classe a un certain nombre de contraintes :
une fois héritée, une classe ne peut pas être changée : on dit que l'héritage est une alté-
ration statique ;
les classes enfants ne peuvent avoir qu'un seul parent en PHP, il n'est donc pas
possible d'étendre une classe SMSNotier et une classe PushNotier en même temps.
Nous en arrivons donc à un réexe à acquérir : privilégier la composition plutôt que la
surcharge.
Cela signie inclure les objets dont nous voulons utiliser le comportement plutôt que les
étendre (et donc dépendre d'eux).
On dit alors que nous "décorons" une classe avec une autre.
Page 5
Le design pattern Decorator
1. L'interface ComponentInterface permet d'avoir une interface commune entre le décorateur
et la classe décorée.
2. Le Component concret est une classe dont le comportement peut être enrichi par d'autres
objets.
3. L'interface DecoratorInterface dénit un champ ComponentInterface qui permet la dé-
coration. Ce décorateur de base délègue toutes les opérations réelles à l'objet
Component.
4. Chaque décorateur concret peut dénir un ou plusieurs comportements qui vont s'exécu-
ter en complément du comportement originel du Component, avant ou après l'appel
de la fonction d'origine (ici, la fonctiondoSomething()).
Implémentons la solution au problème décrit en PHP à l'aide du design pattern Decorator.
Tout d'abord, l'interface du Component (ici, un système de notication) :
<?php
interface NotifierInterface
{
public function notify();
}
Page 6
et l'interface du décorateur, qui en PHP est identique, si l'on ne souhaite pas rendre public l'accès
au Component décoré :
<?php
interface NotifierDecoratorInterface
{
public function notify();
}
Maintenant, créons un décorateur :
<?php
final class SMSNotifier implements ComponentInterface, NotifierDecoratorInterface
{
private $component;
public function __construct(ComponentInterface $component = null)
{
$this->component = $component;
}
public function notify()
{
if ($this->component !== null) {
$this->component->notify();
}
$this->sendSMS();
}
private function sendSMS()
{
//
}
}
Il ne reste plus qu'à mettre en place un client capable d'appeler les diérents décorateurs :
<?php
use NullNotifier;
use SMSNotifier;
Page 7
use EmailNotifier;
use PushNotifier;
// les 3 en même temps
$fullNotifier = new SMSNotifier(new EmailNotifier(new PushNotifier));
// seulement SMS et Push ? facile !
$mobileNotifier = new PushNotifier(new SMSNotifier());
// aucune notification ?
$nullNotifier = new NullNotifier();
class Client
{
private $notifier;
public function __construct(NotifierInterface $notifier)
{
$this->notifier = $notifier;
}
public function notify()
{
return $this->notifier->notify();
}
}
Remarquez que la classe Client ne changera pas en fonction du nombre de systèmes de notication
supportés par l'application, ou en fonction des choix de l'utilisateur : ce code est donc solide.
Page 8