Vous êtes sur la page 1sur 55

Design patterns de structure

en PHP

Alexandre Brabant 1

2024

1/261. Adaptation d'un cours de Mickaël Andrieu (mis à jour le 16/02/2024)


Le design pattern Composite

Le design pattern Decorator

2/26
Le design pattern Composite

3/26
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.

4/26
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 :

4/26
Gouvernement français (représentation vulgarisée)

5/26
Vous imaginez la complexité de représenter cela, par
exemple pour obtenir les réponses aux questions :

6/26
Vous imaginez la complexité de représenter cela, par
exemple pour obtenir les réponses aux questions :

▶ Quels sont les supérieurs et/ou subordonnés d'un


membre du gouvernement ?

6/26
Vous imaginez la complexité de représenter cela, par
exemple pour obtenir les réponses aux questions :

▶ Quels sont les supérieurs et/ou subordonnés d'un


membre du gouvernement ?
▶ Quel est le budget d'un ministère en y rattachant
l'ensemble des salaires de tous les membres le
constituant ?

6/26
Vous imaginez la complexité de représenter cela, par
exemple pour obtenir les réponses aux questions :

▶ Quels sont les supérieurs et/ou subordonnés d'un


membre du gouvernement ?
▶ 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 communes entre ces
diérents objets :

6/26
Le design pattern Composite

7/26
1. L'interface ComponentInterface décrit l'ensemble
des opérations qui sont communes à tous les
éléments de l'arbre.

8/26
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).

8/26
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.

8/26
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.
8/26
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.

9/26
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.

9/26
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.

<?php

interface Employee
{
public function getBudget();
}

9/26
Le président de la République est un Composite (ou un
employeur, si vous préférez).

10/26
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 :

10/26
<?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) {


//
}
}
11/26
Implémentons notre président !

12/26
Implémentons notre président !

<?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;
}
}
12/26
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é :

13/26
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;
}
}
13/26
Enn, voici comment on pourrait "théoriquement"
calculer le budget du ministère de l'Éducation :

14/26
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();

14/26
Le composant Form du framework Symfony utilise le
design pattern Composite.

15/26
Le design pattern Decorator

16/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
un objet sans en modier le code.

17/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
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.

17/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
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 :

17/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
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 ;

17/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
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 ;

17/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
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 ;
17/26
Le design pattern Decorator
Le design pattern Decorator vous permet d'ajouter
dynamiquement de nouveaux comportements à
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 ;
17/26▶ soit... rien du tout.
On peut penser que l'héritage de classe est la meilleure
solution quand on veut altérer le comportement d'un
objet, mais l'héritage de classe a un certain nombre de
contraintes :

18/26
On peut penser que l'héritage de classe est la meilleure
solution quand on veut altérer le comportement 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 ;

18/26
On peut penser que l'héritage de classe est la meilleure
solution quand on veut altérer le comportement 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.

18/26
privi-
Nous en arrivons donc à un réexe à acquérir :
légier la composition plutôt que la surcharge.
Cela signie inclure les objets dont nous voulons uti-
liser 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.

19/26
Le design pattern Decorator
20/26
1. L'interface ComponentInterface permet d'avoir
une interface commune entre le décorateur et la
classe décorée.

21/26
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.

21/26
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.

21/26
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écuter en
complément du comportement originel du
Component, avant ou après l'appel de la fonction
d'origine (ici, la fonctiondoSomething()).
21/26
Implémentons la solution au problème décrit en PHP à
l'aide du design pattern Decorator.

22/26
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) :

22/26
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();
}

22/26
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é :

23/26
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();
}

23/26
Maintenant, créons un décorateur :

24/26
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() {


//
}
}

24/26
Il ne reste plus qu'à mettre en place un client capable
d'appeler les diérents décorateurs :

25/26
Il ne reste plus qu'à mettre en place un client capable
d'appeler les diérents décorateurs :
<?php

use NullNotifier; use SMSNotifier; 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();
}
}
25/26
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.

26/26

Vous aimerez peut-être aussi