Patron Stratégie
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 74 sur 91.
Le Strategy Pattern (ou patron de conception Stratégie) est un patron de conception comportemental qui vous permet de définir une famille d'algorithmes, d'encapsuler chacun d'eux dans sa propre classe et de les rendre interchangeables au moment de l'exécution. L'objet utilisant la stratégie n'a pas besoin de savoir quel algorithme il exécute.
Imaginez un panier d'achat capable d'appliquer différentes stratégies de remise. Au lieu de coder en dur la logique de remise avec des conditions, vous définissez chaque remise comme une classe distincte :
<?php
interface DiscountStrategy {
public function calculate(float $total): float;
}
class NoDiscount implements DiscountStrategy {
public function calculate(float $total): float {
return $total;
}
}
class PercentageDiscount implements DiscountStrategy {
public function __construct(private int $percent) {}
public function calculate(float $total): float {
return $total - ($total * $this->percent / 100);
}
}
class FixedDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return max(0, $total - $this->amount);
}
}
La classe de contexte accepte n'importe quelle stratégie et délègue le calcul :
<?php
class ShoppingCart {
public function __construct(private DiscountStrategy $discount) {}
public function setDiscount(DiscountStrategy $discount): void {
$this->discount = $discount;
}
public function checkout(float $total): float {
return $this->discount->calculate($total);
}
}
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";
$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);
Sortie :
100
80Le pattern Strategy brille lorsque vous avez plusieurs façons d'effectuer une opération et que vous souhaitez basculer entre elles sans modifier la classe qui les utilise. Ajouter un nouveau type de remise signifie créer une nouvelle classe, sans toucher au code existant.
Défi
FacileConstruisons un calculateur de frais d'expédition en utilisant le Strategy Pattern. Différentes méthodes d'expédition ont des règles de tarification différentes — l'expédition standard facture un tarif forfaitaire, l'expédition express ajoute un pourcentage, et l'expédition de nuit combine les deux. Le Strategy Pattern vous permet d'interchanger ces calculs sans modifier le code qui les utilise.
Vous organiserez votre code sur quatre fichiers :
ShippingStrategy.php— Créez une interfaceShippingStrategyqui définit le contrat pour toutes les méthodes d'expédition. Elle doit avoir une méthodecalculate(float $weight): floatqui prend le poids du colis et retourne le coût d'expédition.ShippingMethods.php— Incluez le fichier ShippingStrategy et créez trois classes qui implémentent l'interface :StandardShipping— Facture un tarif forfaitaire de$5.00quel que soit le poidsExpressShipping— Facture$2.00par kilogramme (multipliez le poids par 2)OvernightShipping— Facture des frais de base de$10.00plus$3.00par kilogramme
ShippingCalculator.php— Incluez le fichier ShippingStrategy et créez une classeShippingCalculatorqui agit comme contexte. Votre calculateur doit :- Accepter une
ShippingStrategydans son constructeur (utilisez la promotion de constructeur) - Avoir une méthode
setStrategy(ShippingStrategy $strategy): voidpour changer la méthode d'expédition au moment de l'exécution - Avoir une méthode
calculateCost(float $weight): floatqui délègue à la stratégie actuelle
- Accepter une
main.php— Incluez les fichiers ShippingMethods et ShippingCalculator. Vous recevrez deux entrées : un type d'expédition ("standard","express", ou"overnight") et un poids de colis.Créez un
ShippingCalculatoren commençant parStandardShipping. En fonction du type d'expédition saisi, utilisezsetStrategy()pour passer à la méthode d'expédition appropriée. Calculez ensuite le coût pour le poids donné et affichez le résultat formaté avec deux décimales.La sortie doit être :
Shipping cost: $[cost]
Ce défi démontre comment le Strategy Pattern rend votre système d'expédition flexible — l'ajout d'une nouvelle méthode d'expédition comme SameDayShipping nécessite seulement la création d'une nouvelle classe qui implémente l'interface, sans toucher au calculateur ou aux méthodes d'expédition existantes.
Aide-mémoire
Le Strategy Pattern (ou patron de stratégie) est un patron de conception comportemental qui définit une famille d'algorithmes, encapsule chacun d'eux dans sa propre classe et les rend interchangeables au moment de l'exécution.
Définissez une interface pour la stratégie :
<?php
interface DiscountStrategy {
public function calculate(float $total): float;
}Implémentez des stratégies concrètes :
<?php
class NoDiscount implements DiscountStrategy {
public function calculate(float $total): float {
return $total;
}
}
class PercentageDiscount implements DiscountStrategy {
public function __construct(private int $percent) {}
public function calculate(float $total): float {
return $total - ($total * $this->percent / 100);
}
}
class FixedDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return max(0, $total - $this->amount);
}
}Créez une classe de contexte qui utilise la stratégie :
<?php
class ShoppingCart {
public function __construct(private DiscountStrategy $discount) {}
public function setDiscount(DiscountStrategy $discount): void {
$this->discount = $discount;
}
public function checkout(float $total): float {
return $this->discount->calculate($total);
}
}Utilisez le patron en injectant des stratégies :
<?php
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100); // 100
$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100); // 80Le Strategy Pattern vous permet de basculer entre les algorithmes sans modifier la classe qui les utilise. L'ajout de nouvelles stratégies nécessite uniquement la création de nouvelles classes qui implémentent l'interface.
Essayez vous-même
<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';
// Lire l'entrée
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));
// TODO: Créer un ShippingCalculator en commençant par StandardShipping
// TODO: En fonction du type d'expédition saisi ("standard", "express" ou "overnight"),
// utiliser setStrategy() pour passer à la méthode d'expédition appropriée
// TODO: Calculer le coût pour le poids donné
// TODO: Afficher le résultat formaté avec deux décimales
// Format de sortie : Shipping cost: $[cost]
?>Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Les bases de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition de méthodeLe mot-clé finalClasses abstraitesRécapitulatif - Hiérarchie des employés7Encapsulation
Public, Protégé, PrivéModificateurs d'accès en détailGetters et SettersMasquage d'informationPromotion de constructeur (8.0)Récapitulatif - Système de gestion d'étudiants10Concepts avancés de la POO
Composition vs HéritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibers (PHP 8.1)Clonage d'objets : analyse approfondieGénérateurs et Itérateurs13Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository2Espaces de noms et Autoloading
Introduction aux espaces de nomsLe mot-clé useStandard d'Autoloading PSR-4L'Autoloader ComposerRécapitulatif - Projet organisé5Interfaces et Contrats
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesInterface vs Classe AbstraiteType Hinting avec les InterfacesRécapitulatif - Calculateur de Formes8Méthodes magiques
Introduction aux méthodes magiques__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Clonage d'objets__serialize & __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d'instance vs Propriétés statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés readonly (PHP 8.1)Récapitulatif - Gestionnaire de compte bancaire6Polymorphisme
Retour sur la redéfinition de méthodePolymorphisme via les interfacesType Hinting et Types d'unionLiaison statique tardiveRécapitulatif - Processeur de paiement9Traits
Introduction aux TraitsUtilisation de plusieurs TraitsRésolution des conflits de TraitsMéthodes abstraites dans les TraitsTraits vs Héritage12Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie