Programmation orientée objet (POO) en PHP

Auteur: Mohamed CHINY Durée necessaire pour le cours de Programmation orientée objet (POO) en PHP Niveau recommandé pour le cours de Programmation orientée objet (POO) en PHP Supports vidéo disponibles pour ce cours Exercices de renforcement disponibles pour ce cours Quiz non disponibles pour ce cours

Leçon 16: Introduction aux Design Patterns en PHP: pourquoi et quand les utiliser?

Toutes les leçons

Comprendre les patrons de conception - Design Patterns

Pourquoi les design patterns sont utiles?

Lorsqu’on développe des applications en PHP orienté objet, on rencontre souvent des problèmes récurrents:
  • Comment garantir qu’une classe n’ait qu’une seule instance?
  • Comment créer des objets sans exposer la complexité de leur construction?
  • Comment notifier automatiquement plusieurs parties d’un système lorsqu’un événement se produit?

Ces difficultés ne sont pas propres à PHP, elles apparaissent dans tous les langages orientés objet.

Pour y répondre, le Gang of Four (GoF) (quatre auteurs fondateurs) ont proposé en 1994 un catalogue de 23 patrons de conception regroupés en trois familles (créationnels, structurels et comportementaux). Ces solutions éprouvées permettent de rendre le code plus clair, plus maintenable et surtout plus compréhensible, car elles constituent un langage commun entre développeurs.
Dans cette leçon, nous allons nous concentrer sur 5 patterns essentiels: Singleton, Factory, Observer, Strategy et Adapter. Chacun sera présenté avec son problème, sa définition et un exemple simple en PHP, afin de poser une base solide pour comprendre et appliquer ces concepts.

Singleton: garantir l’unicité d’une instance

Le problème du Singleton apparaît lorsqu’on veut garantir qu’une classe n’ait qu’une seule instance dans toute l’application. Imaginez par exemple un gestionnaire de configuration ou une connexion à la base de données: il serait dangereux et coûteux d’en créer plusieurs copies. Le Singleton résout ce problème en imposant une seule instance accessible globalement.

En PHP, on implémente ce pattern en rendant le constructeur privé et en fournissant une méthode statique qui retourne toujours la même instance. Ainsi, chaque fois qu’on appelle getInstance(), on obtient l’unique objet existant:
class Config {
   private static $instance = null;
   private function __construct() {}
   public static function getInstance() {
      if (self::$instance === null) {
         self::$instance = new Config();
      }
      return self::$instance;
   }
}
$config = Config::getInstance();

Factory Method: simplifier et centraliser la création d’objets

Le problème du Factory Method est lié à la création d’objets. Dans une application complexe, il est souvent nécessaire de créer des objets de différents types, mais on ne veut pas exposer la logique interne de leur construction. Le Factory Method propose de déléguer cette responsabilité à une méthode spécialisée, appelée "fabrique".

En PHP, on peut définir une classe ShapeFactory qui se charge de créer les objets selon le type demandé. L’avantage est que le code client n’a pas besoin de connaître les détails de chaque classe, mais il se contente d’appeler la fabrique:
interface Shape {
   public function draw();
}
class Circle implements Shape {
   public function draw() { echo "Cercle" }
}
class ShapeFactory {
   public static function create($type) {
      if ($type === "circle") return new Circle();
   }
}
$shape = ShapeFactory::create("circle");
$shape->draw();

Observer: notifier automatiquement plusieurs objets lors d’un changement

Le problème du Observer survient lorsqu’un changement dans un objet doit être automatiquement communiqué à plusieurs autres objets. Par exemple, dans un système de notifications, lorsqu’un utilisateur publie un message, plusieurs abonnés doivent être informés.

Le Observer définit une relation un‑à‑plusieurs: un objet "sujet" garde une liste d’observateurs et les prévient lorsqu’un événement se produit. En PHP, cela se traduit par une classe Subject qui gère les observateurs et une méthode notify() qui les alerte:
class Subject {
   private $observers = [];
   public function attach($obs) {
      $this->observers[] = $obs;
   }
   public function notify() {
      foreach ($this->observers as $obs)
         $obs->update();
   }
}
class Observer {
   public function update() {
      echo "Notification reçue";
   }
}
$subject = new Subject();
$subject->attach(new Observer());
$subject->notify();

Strategy: rendre les algorithmes interchangeables et flexibles

Le problème du Strategy apparaît lorsqu’on veut pouvoir changer facilement d’algorithme sans modifier le code principal. Par exemple, dans un système de calcul de réduction, on peut avoir plusieurs stratégies: réduction en pourcentage, réduction fixe, etc.

Le Strategy encapsule chaque algorithme dans une classe distincte et permet de les remplacer dynamiquement. En PHP, on définit une interface DiscountStrategy et plusieurs implémentations. Le contexte choisit la stratégie à utiliser et peut la changer à tout moment:
interface DiscountStrategy {
   public function apply($price);
}
class PercentageDiscount implements DiscountStrategy {
   public function apply($price) {
      return $price * 0.9;
   }
}
class Context {
   private $strategy;
   public function __construct(DiscountStrategy $s) {
      $this->strategy = $s;
   }
   public function getPrice($p) {
      return $this->strategy->apply($p);
   }
}
$context = new Context(new PercentageDiscount());
echo $context->getPrice(100);

Adapter: assurer la compatibilité entre interfaces différentes

Le problème du Adapter se pose lorsqu’on veut utiliser deux systèmes qui n’ont pas la même interface. Par exemple, une ancienne classe qui ne correspond pas aux besoins actuels, ou une API externe qu’on veut intégrer sans modifier son code.

Le Adapter agit comme un traducteur: il convertit l’interface d’une classe pour la rendre compatible avec une autre. En PHP, on crée une classe intermédiaire qui appelle les méthodes de l’ancien système mais expose une nouvelle interface adaptée:
class OldSystem {
   public function request() {
      echo "Ancien système";
   }
}
class Adapter {
   private $old;
   public function __construct(OldSystem $o) {
      $this->old = $o;
   }
   public function newRequest() {
      $this->old->request();
   }
}
$adapter = new Adapter(new OldSystem());
$adapter->newRequest();
Le MVC (Model‑View‑Controller) est l’un des patrons d’architecture les plus utilisés dans le développement web moderne. Il ne fait pas partie des 23 patrons de conception du Gang of Four, mais il s’inspire de leurs principes. Le MVC sera étudié plus en détail dans une leçon ultérieure, car il mérite une présentation approfondie en tant que structure globale d’application.