Patron État
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 79 sur 91.
Le State Pattern est un patron de conception comportemental qui permet à un objet de modifier son comportement lorsque son état interne change. Au lieu d'utiliser des conditions complexes pour gérer différents états, vous encapsulez chaque état dans sa propre classe et déléguez le comportement à l'objet d'état actuel.
Pensez à un distributeur automatique. Il se comporte différemment selon qu'il attend de l'argent, qu'il a reçu un paiement ou qu'il distribue un produit.
Chaque état possède ses propres règles concernant les actions autorisées. Le patron de conception État (State Pattern) modélise cela en créant une classe pour chaque état.
<?php
interface OrderState {
public function proceed(Order $order): string;
public function getStatus(): string;
}
class PendingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ProcessingState());
return "Order moved to processing";
}
public function getStatus(): string {
return "Pending";
}
}
class ProcessingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ShippedState());
return "Order has been shipped";
}
public function getStatus(): string {
return "Processing";
}
}
class ShippedState implements OrderState {
public function proceed(Order $order): string {
return "Order already shipped";
}
public function getStatus(): string {
return "Shipped";
}
}
La classe de contexte détient une référence à l'état actuel et lui délègue les appels :
<?php
class Order {
private OrderState $state;
public function __construct() {
$this->state = new PendingState();
}
public function setState(OrderState $state): void {
$this->state = $state;
}
public function proceed(): string {
return $this->state->proceed($this);
}
public function getStatus(): string {
return $this->state->getStatus();
}
}
$order = new Order();
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus();
Sortie :
Pending
Order moved to processing
Processing
Order has been shipped
ShippedLe State Pattern élimine les instructions switch complexes et rend l'ajout de nouveaux états simple. Chaque classe d'état est responsable de savoir quel état suit, ce qui permet de garder la logique organisée et facile à maintenir.
Défi
FacileConstruisons un système de flux de travail de documents en utilisant le State Pattern. Les documents dans un système de gestion de contenu passent par différentes étapes — brouillon, révision et publié — chaque étape ayant ses propres règles sur les actions autorisées et sur ce qui se passe lorsque vous essayez de faire avancer le document.
Vous organiserez votre code sur quatre fichiers :
DocumentState.php— Définissez une interfaceDocumentStatequi établit le contrat pour tous les états de document. Elle doit avoir deux méthodes :publish(Document $document): stringpour tenter de faire avancer le document, etgetStatus(): stringpour retourner le nom de l'état actuel.States.php— Incluez l'interface DocumentState et créez trois classes d'état qui l'implémentent :DraftState— Lorsquepublish()est appelée, elle fait passer le document àReviewStateet retourne"Document sent for review". Son statut est"Draft".ReviewState— Lorsquepublish()est appelée, elle fait passer le document àPublishedStateet retourne"Document published". Son statut est"Under Review".PublishedState— Lorsquepublish()est appelée, elle retourne"Document is already published"sans changer d'état. Son statut est"Published".
Document.php— Incluez le fichier States et créez une classeDocumentqui agit comme contexte. Votre document doit :- Accepter un titre dans son constructeur et commencer dans l'état
DraftState - Avoir une méthode
setState(DocumentState $state): voidpour changer l'état actuel - Avoir une méthode
publish(): stringqui délègue à la méthodepublish()de l'état actuel - Avoir une méthode
getStatus(): stringqui retourne le statut de l'état actuel
- Accepter un titre dans son constructeur et commencer dans l'état
main.php— Incluez le fichier Document. Vous recevrez une entrée : le nombre de fois qu'il faut appelerpublish()sur le document.Créez un
Documentavec le titre"My Article". Tout d'abord, affichez le statut initial. Ensuite, appelezpublish()le nombre de fois spécifié, en affichant le résultat de chaque appel sur une nouvelle ligne. Enfin, affichez le statut final.Format de sortie :
Status: [initial status] [publish result 1] [publish result 2] ... Status: [final status]
Chaque classe d'état sait quel état vient ensuite dans le flux de travail, gardant la logique de transition organisée et facile à modifier. Ajouter un nouvel état comme "Archived" signifierait simplement créer une nouvelle classe et mettre à jour les transitions concernées.
Aide-mémoire
Le State Pattern (Patron d'état) est un patron de conception comportemental qui permet à un objet de changer son comportement lorsque son état interne change en encapsulant chaque état dans sa propre classe.
Définissez une interface d'état que toutes les classes d'état implémenteront :
<?php
interface OrderState {
public function proceed(Order $order): string;
public function getStatus(): string;
}Créez des classes d'état concrètes qui implémentent l'interface :
<?php
class PendingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ProcessingState());
return "Order moved to processing";
}
public function getStatus(): string {
return "Pending";
}
}
class ProcessingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ShippedState());
return "Order has been shipped";
}
public function getStatus(): string {
return "Processing";
}
}
class ShippedState implements OrderState {
public function proceed(Order $order): string {
return "Order already shipped";
}
public function getStatus(): string {
return "Shipped";
}
}Créez une classe de contexte qui détient une référence à l'état actuel et lui délègue les appels :
<?php
class Order {
private OrderState $state;
public function __construct() {
$this->state = new PendingState();
}
public function setState(OrderState $state): void {
$this->state = $state;
}
public function proceed(): string {
return $this->state->proceed($this);
}
public function getStatus(): string {
return $this->state->getStatus();
}
}Exemple d'utilisation :
$order = new Order();
echo $order->getStatus() . "\n"; // Pending
echo $order->proceed() . "\n"; // Order moved to processing
echo $order->getStatus() . "\n"; // Processing
echo $order->proceed() . "\n"; // Order has been shipped
echo $order->getStatus(); // ShippedLe State Pattern élimine les instructions switch complexes et rend l'ajout de nouveaux états simple. Chaque classe d'état est responsable de savoir quel état vient ensuite.
Essayez vous-même
<?php
require_once 'Document.php';
// Lire l'entrée - nombre de fois qu'il faut appeler publish()
$n = intval(trim(fgets(STDIN)));
// TODO: Créer un Document avec le titre "My Article"
// TODO: Afficher le statut initial au format : "Status: [status]"
// TODO: Appeler publish() n fois, en affichant chaque résultat sur une nouvelle ligne
// TODO: Afficher le statut final au format : "Status: [status]"
?>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