Créa-blog

Ressources pour développeur web

Les design patterns en programmation orientée objet en PHP

Accueil PHP 8 Les design patterns en programmation orientée objet en PHP

Les design patterns, ou motifs de conception, sont des solutions éprouvées aux problèmes courants rencontrés lors de la conception d’applications en PHP. En programmation orientée objet en PHP, l’utilisation des design patterns permet de créer des systèmes plus robustes, plus flexibles et plus évolutifs.

Qu’est ce qu’un design pattern ?

Imaginez que vous construisez une maison. Vous pouvez avoir différents plans ou modèles pour construire des pièces comme la cuisine, la chambre à coucher ou la salle de bains. Ces modèles vous aident à organiser et à construire chaque pièce de manière cohérente et efficace.

En programmation orientée objet (POO), un design pattern est comme un plan ou un modèle pour résoudre un problème commun de conception d’applications. Ces patterns fournissent des solutions testées et éprouvées à des problèmes récurrents rencontrés lors du développement.

Par exemple, prenons le pattern Singleton. Il est utilisé lorsque vous avez besoin d’une classe qui ne peut avoir qu’une seule instance dans votre application. Un exemple concret serait une classe de connexion à la base de données. Vous ne voulez pas créer plusieurs instances de cette classe car cela pourrait entraîner des problèmes de performances ou de cohérence des données.

class DatabaseConnection {
    private static $instance;

    private function __construct() {
        // Code de connexion à la base de données
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // Méthodes pour interagir avec la base de données
}

Dans cet exemple, getInstance() est une méthode statique qui crée une instance unique de la classe DatabaseConnection et la retourne. Vous pouvez ainsi utiliser cette classe partout dans votre application, en vous assurant qu’il n’y aura qu’une seule connexion à la base de données.

Les design patterns en POO en PHP vous aident à organiser et à structurer votre code de manière efficace, en vous permettant de réutiliser des concepts bien établis et testés. Cela rend votre code plus facile à comprendre, à maintenir et à étendre.

Le mot-clé static

Le mot-clé static en PHP est utilisé pour définir des propriétés et des méthodes de classe. Lorsqu’une propriété ou une méthode est déclarée comme étant static, cela signifie qu’elle est associée à la classe elle-même plutôt qu’à une instance spécifique de cette classe. En d’autres termes, vous pouvez accéder à une propriété statique ou à une méthode statique sans avoir besoin d’instancier la classe.

Voici ce que cela signifie en pratique :

Propriétés statiques : Une propriété statique est partagée entre toutes les instances de la classe. Cela signifie que si vous modifiez la valeur d’une propriété statique dans une instance, cette modification sera reflétée dans toutes les autres instances et dans la classe elle-même.

class Exemple {
    public static $compteur = 0;

    public function incrementerCompteur() {
        self::$compteur++;
    }
}

Exemple::$compteur = 10;
echo Exemple::$compteur; // Affiche : 10

Méthodes statiques : Une méthode statique peut être appelée directement sur la classe, sans avoir besoin d’instancier la classe. Ces méthodes sont souvent utilisées pour des fonctionnalités qui ne dépendent pas de l’état de l’instance.

class MathUtil {
    public static function additionner($a, $b) {
        return $a + $b;
    }
}

echo MathUtil::additionner(5, 3); // Affiche : 8

Le mot-clé static en PHP est utilisé pour définir des propriétés et des méthodes de classe qui sont partagées entre toutes les instances de cette classe et qui peuvent être appelées directement sur la classe elle-même, sans avoir besoin d’instancier la classe.

Le mot-clé self::

Le mot-clé self:: en PHP est utilisé pour accéder à des propriétés statiques ou à des méthodes statiques à l’intérieur de la classe elle-même. Il fait référence à la classe dans laquelle il est utilisé, indépendamment de l’instance spécifique de cette classe.

Lorsque vous utilisez self::, vous faites référence à la classe elle-même, et non à une instance particulière de cette classe. Cela signifie que vous pouvez accéder aux propriétés et aux méthodes statiques de la classe, ainsi qu’appeler d’autres méthodes statiques ou accéder à d’autres propriétés statiques.

Voici un exemple d’utilisation de self:: pour accéder à une propriété statique et appeler une méthode statique à l’intérieur de la classe :

class Exemple {
    public static $proprieteStatique = 10;

    public static function afficherProprieteStatique() {
        echo self::$proprieteStatique;
    }
}

Exemple::afficherProprieteStatique(); // Affiche : 10

Dans cet exemple, self::$proprieteStatique est utilisé pour accéder à la propriété statique $proprieteStatique de la classe Exemple, et self::afficherProprieteStatique() est utilisé pour appeler la méthode statique afficherProprieteStatique() de la même classe.

self:: en PHP est utilisé pour faire référence à des propriétés et des méthodes statiques à l’intérieur de la classe elle-même, en accédant à la classe de manière statique plutôt qu’à une instance spécifique de cette classe.

Exemple de design pattern : Le Singleton

Imaginez un garde du corps personnel. Peu importe combien de fois vous appelez pour obtenir une protection, vous obtiendrez toujours le même garde du corps. C’est un peu comme ça que fonctionne le pattern Singleton en programmation.

Le pattern Singleton est un design pattern qui garantit qu’une classe n’a qu’une seule instance dans toute l’application. Il est utile lorsque vous avez besoin d’un objet qui doit être partagé par plusieurs parties de votre programme et que vous voulez éviter d’avoir plusieurs instances de cet objet.

Voici comment cela fonctionne en pratique :

class Singleton {
    // Variable statique pour stocker l'instance unique de la classe
    private static $instance;

    // Constructeur privé pour empêcher l'instanciation directe de la classe
    private function __construct() {
        // Initialisation de l'objet singleton
    }

    // Méthode statique pour obtenir l'instance unique de la classe
    public static function getInstance() {
        // Vérifie si l'instance n'a pas déjà été créée
        if (self::$instance === null) {
            // Crée une nouvelle instance de la classe si elle n'existe pas encore
            self::$instance = new self();
        }
        // Retourne l'instance unique de la classe
        return self::$instance;
    }

    // Autres méthodes de la classe
}

Dans cet exemple, nous avons une classe Singleton qui a une méthode statique getInstance(). Lorsque vous appelez Singleton::getInstance(), la méthode vérifie si une instance de la classe a déjà été créée. Si ce n’est pas le cas, elle crée une nouvelle instance en utilisant le constructeur privé de la classe. Sinon, elle retourne simplement l’instance existante.

Voici comment vous pouvez utiliser ce Singleton :

$singleton1 = Singleton::getInstance();
$singleton2 = Singleton::getInstance();

// Les deux variables pointent vers la même instance de la classe Singleton
var_dump($singleton1 === $singleton2); // Affiche : bool(true)

Dans cet exemple, $singleton1 et $singleton2 font référence à la même instance de la classe Singleton. Cela garantit qu’il n’y a qu’une seule instance de la classe dans toute l’application.

Le pattern Singleton en POO est utilisé pour garantir qu’une classe n’a qu’une seule instance dans toute l’application. Il est souvent utilisé pour des objets qui doivent être partagés et accessibles de manière globale dans le programme.

Exemple de design pattern : Le Factory

Imaginez que vous vouliez commander une pizza. Plutôt que de faire la pizza vous-même à partir de zéro, vous appelez une pizzeria qui se spécialise dans la fabrication de pizzas. La pizzeria a différentes recettes de pizzas et peut créer la pizza que vous voulez sans que vous ayez à vous soucier des détails de fabrication. C’est un peu comme ça que fonctionne le pattern Factory en programmation.

Le pattern Factory est un design pattern qui fournit un moyen de créer des objets sans spécifier la classe exacte de l’objet à créer. Au lieu de cela, il utilise des méthodes de fabrique pour instancier les objets en fonction de certaines conditions ou paramètres.

Voici comment cela fonctionne en pratique :

interface Produit {
    public function operation();
}

class ProduitA implements Produit {
    public function operation() {
        echo "Opération produit A\n";
    }
}

class ProduitB implements Produit {
    public function operation() {
        echo "Opération produit B\n";
    }
}

class FabriqueProduit {
    public function creerProduit($type) {
        // Condition pour décider quelle classe de produit créer
        if ($type === 'A') {
            return new ProduitA();
        } elseif ($type === 'B') {
            return new ProduitB();
        }
        // Retourne null si le type de produit n'est pas reconnu
        return null;
    }
}

Dans cet exemple, nous avons une interface Produit avec une méthode operation(), ainsi que deux classes ProduitA et ProduitB qui implémentent cette interface. Ensuite, nous avons une classe FabriqueProduit avec une méthode creerProduit($type) qui crée un objet de type Produit en fonction du paramètre $type passé.

Voici comment vous pouvez utiliser cette fabrique de produits :

$fabrique = new FabriqueProduit();
$produitA = $fabrique->creerProduit('A');
$produitB = $fabrique->creerProduit('B');

$produitA->operation(); // Affiche : "Opération produit A"
$produitB->operation(); // Affiche : "Opération produit B"

Dans cet exemple, nous utilisons la fabrique de produits pour créer un produit de type A et un produit de type B. La fabrique décide quelle classe de produit créer en fonction du type passé en paramètre.

Le pattern Factory en POO est utilisé pour créer des objets sans spécifier la classe exacte de l’objet à créer. Il offre un moyen flexible et décentralisé de créer des objets en utilisant des méthodes de fabrique. Cela rend votre code plus modulaire et plus facile à étendre, car vous pouvez ajouter de nouvelles classes de produits sans avoir à modifier le code existant.

Exemple de design pattern : L’Observer

Le pattern Observer, ou Observateur, est un concept utilisé en programmation pour gérer les dépendances entre les objets de manière dynamique. Imaginez une situation où vous avez un objet qui change de temps en temps, et vous avez d’autres objets qui doivent être informés de ces changements et réagir en conséquence. C’est là que le pattern Observer intervient.

Pour comprendre le pattern Observer, prenons l’exemple d’un bulletin météo. Vous avez une station météo qui recueille les données météorologiques, et vous avez plusieurs écrans dans différentes pièces de votre maison qui affichent ces données. Lorsque les données météorologiques changent, tous les écrans doivent être mis à jour pour refléter ces changements. Dans ce scénario, la station météo est le sujet observé et les écrans sont les observateurs.

Voici comment cela fonctionne en programmation :

Le Sujet Observé (Subject) :
C’est l’objet qui détient l’état qui peut changer. Il a une liste d’observateurs et fournit des méthodes pour ajouter, supprimer et notifier les observateurs lorsque son état change.

Les Observateurs (Observers) :
Ce sont les objets qui sont intéressés par les changements d’état du sujet observé. Ils ont une méthode de mise à jour qui est appelée lorsque le sujet observé les notifie d’un changement d’état.

  1. Vous créez une interface Observable que votre objet station météo implémente. Cette interface définit des méthodes comme ajouterObservateur pour ajouter un observateur à la liste et supprimerObservateur pour le retirer.
  2. Vous créez une interface Observateur que vos différents observateurs implémentent. Cette interface définit une méthode actualiser qui sera appelée par le sujet lorsqu’un changement survient.
  3. Votre objet station météo maintient une liste d’observateurs.
  4. Lorsque de nouvelles données sont disponibles, la station météo les met à jour et appelle la méthode actualiser sur chaque observateur dans sa liste.

Cela permet à chaque observateur d’être informé automatiquement des changements de données météorologiques sans que la station météo ait besoin de connaître les détails spécifiques de chaque observateur.

Par exemple, vous pouvez avoir un afficheur qui affiche simplement les données mises à jour à l’écran, un enregistreur qui enregistre les données dans un fichier, ou un alerteur qui déclenche une alarme en cas de conditions météorologiques dangereuses.

Ensemble, le pattern Observer permet une bonne séparation des préoccupations et favorise la réutilisabilité du code.

<?php

// Interface pour les observateurs
interface Observateur {
    public function actualiser(float $temperature, float $humidite, float $pression);
}

// Classe pour l'afficheur
class Afficheur implements Observateur {
    public function actualiser(float $temperature, float $humidite, float $pression) {
        echo "Nouvelles données météorologiques : Température $temperature °C, Humidité $humidite %, Pression $pression hPa\n";
    }
}

// Classe pour la station météorologique (sujet observable)
class StationMeteo {
    private $observateurs = [];
    private $temperature;
    private $humidite;
    private $pression;

    public function ajouterObservateur(Observateur $observateur) {
        $this->observateurs[] = $observateur;
    }

    public function supprimerObservateur(Observateur $observateur) {
        $index = array_search($observateur, $this->observateurs);
        if ($index !== false) {
            unset($this->observateurs[$index]);
        }
    }

    public function actualiserMesures(float $temperature, float $humidite, float $pression) {
        $this->temperature = $temperature;
        $this->humidite = $humidite;
        $this->pression = $pression;
        $this->notifierObservateurs();
    }

    private function notifierObservateurs() {
        foreach ($this->observateurs as $observateur) {
            $observateur->actualiser($this->temperature, $this->humidite, $this->pression);
        }
    }
}

// Test de l'exemple
$stationMeteo = new StationMeteo();
$afficheur = new Afficheur();

$stationMeteo->ajouterObservateur($afficheur);

// Simulation de nouvelles données météorologiques
$stationMeteo->actualiserMesures(20.5, 65, 1013.2);

// Sortie :
// Nouvelles données météorologiques : Température 20.5 °C, Humidité 65 %, Pression 1013.2 hPa

Dans cet exemple, la classe StationMeteo est le sujet observable. Les objets Afficheur sont les observateurs. Lorsque de nouvelles données météorologiques sont disponibles, la méthode actualiserMesures de la station météorologique est appelée pour mettre à jour les mesures, et elle notifie ensuite tous les observateurs en appelant leur méthode actualiser, ce qui affiche les nouvelles données météorologiques.

D’autres design pattern populaire

  • Prototype : Crée de nouveaux objets en copiant un prototype existant plutôt qu’en instanciant une nouvelle classe.
  • Adaptateur (Adapter) : Permet à des interfaces incompatibles de travailler ensemble en agissant comme un pont entre elles.
  • Stratégie (Strategy) : Définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables.
  • Décorateur (Decorator) : Attache dynamiquement des responsabilités supplémentaires à un objet de manière transparente.
  • Chaîne de responsabilité (Chain of Responsibility) : Permet de passer une requête le long d’une chaîne de traitements potentiels jusqu’à ce qu’elle soit traitée.
  • Commande (Command) : Encapsule une requête en tant qu’objet, permettant ainsi de paramétrer des clients avec différentes requêtes, de mettre en file d’attente ou d’annuler des requêtes.
  • État (State) : Permet à un objet de modifier son comportement lorsque son état interne change.
  • Interpréteur (Interpreter) : Fournit une façon d’évaluer une expression de langage définie dans un langage donné.