Modello State
Fa parte della sezione Programmazione orientata agli oggetti del percorso PHP di Coddy. Lezione 79 di 91.
Lo State Pattern è un pattern di progettazione comportamentale che consente a un oggetto di cambiare il proprio comportamento quando cambia il suo stato interno. Invece di usare condizioni complesse per gestire stati diversi, incapsuli ogni stato nella propria classe e deleghi il comportamento all’oggetto che rappresenta lo stato corrente.
Pensa a un distributore automatico. Si comporta in modo diverso a seconda che sia in attesa di denaro, abbia ricevuto il pagamento o stia erogando un prodotto.
Ogni stato ha le proprie regole per le azioni consentite. Il pattern State modella questo comportamento creando una classe per ogni stato.
<?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 contesto conserva un riferimento allo stato corrente e gli delega le chiamate:
<?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();
Output:
Pending
Order moved to processing
Processing
Order has been shipped
ShippedIl Pattern State elimina le istruzioni switch troppo articolate e rende semplice aggiungere nuovi stati. Ogni classe di stato è responsabile di conoscere quale stato viene dopo, mantenendo la logica organizzata e facile da gestire.
Sfida
FacileCostruiamo un sistema di flusso di lavoro per documenti usando lo State Pattern. I documenti in un sistema di gestione dei contenuti attraversano diverse fasi: bozza, revisione e pubblicazione; ogni fase ha le proprie regole riguardo alle azioni consentite e a ciò che accade quando provi a far avanzare il documento.
Organizzerai il codice in quattro file:
DocumentState.php: definisci un'interfacciaDocumentStateche stabilisce il contratto per tutti gli stati del documento. Dovrebbe avere due metodi:publish(Document $document): stringper tentare di far avanzare il documento egetStatus(): stringper restituire il nome dello stato corrente.States.php: includi l'interfaccia DocumentState e crea tre classi di stato che la implementano:DraftState: quando viene chiamatopublish(), il documento passa aReviewStatee il metodo restituisce"Document sent for review". Il suo stato è"Draft".ReviewState: quando viene chiamatopublish(), il documento passa aPublishedStatee il metodo restituisce"Document published". Il suo stato è"Under Review".PublishedState: quando viene chiamatopublish(), il metodo restituisce"Document is already published"senza modificare lo stato. Il suo stato è"Published".
Document.php: includi il file States e crea una classeDocumentche funge da contesto. Il tuo documento dovrà:- Accettare un titolo nel costruttore e iniziare nello stato
DraftState - Avere un metodo
setState(DocumentState $state): voidper cambiare lo stato corrente - Avere un metodo
publish(): stringche delega al metodopublish()dello stato corrente - Avere un metodo
getStatus(): stringche restituisce lo stato corrente
- Accettare un titolo nel costruttore e iniziare nello stato
main.php: includi il file Document. Riceverai un input: il numero di volte in cui chiamarepublish()sul documento.Crea un
Documentcon il titolo"My Article". Per prima cosa, stampa lo stato iniziale. Poi, chiamapublish()il numero di volte specificato, stampando il risultato di ogni chiamata su una nuova riga. Infine, stampa lo stato finale.Formato dell'output:
Status: [initial status] [publish result 1] [publish result 2] ... Status: [final status]
Ogni classe di stato sa quale stato viene dopo nel flusso di lavoro, mantenendo la logica delle transizioni organizzata e facile da modificare. Aggiungere un nuovo stato come "Archived" significherebbe semplicemente creare una nuova classe e aggiornare le transizioni pertinenti.
Provalo tu
<?php
require_once 'Document.php';
// Leggi l'input - numero di volte in cui chiamare publish()
$n = intval(trim(fgets(STDIN)));
// TODO: Crea un Document con il titolo "My Article"
// TODO: Stampa lo stato iniziale nel formato: "Status: [status]"
// TODO: Chiama publish() n volte, stampando ogni risultato su una nuova riga
// TODO: Stampa lo stato finale nel formato: "Status: [status]"
?>Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave $thisMetodiProprietàCostruttore (__construct)Distruttore (__destruct)Riepilogo - Calcolatrice semplice4Ereditarietà
Ereditarietà di baseLa parola chiave parent::Sovrascrittura dei metodiLa parola chiave finalClassi astratteRiepilogo - Gerarchia dei dipendenti7Incapsulamento
Pubblico, Protetto, PrivatoApprofondimento sui modificatori di accessoGetter e SetterOccultamento delle informazioniPromozione dei parametri del costruttore (8.0)Riepilogo - Sistema di gestione dei registri degli studenti10Concetti avanzati di programmazione a oggetti
Composizione vs ereditarietàDependency InjectionClassi anonimeEnumerazioni (PHP 8.1)Fibers (PHP 8.1)Approfondimento sulla clonazione degli oggettiGeneratori e iteratori13Modelli di progettazione, parte 2
Modello CommandModello AdapterModello DecoratorModello Template MethodModello StateModello CompositeModello Repository2Namespace e autoloading
Introduzione ai namespaceLa keyword useStandard di autoloading PSR-4Autoloader di ComposerRiepilogo - Progetto organizzato5Interfacce e contratti
Introduzione alle interfacceImplementare le interfacceImplementare più interfacceInterfaccia vs classe astrattaType hinting con le interfacceRiepilogo - Calcolatore di forme8Metodi magici
Introduzione ai metodi magici__toString e __debugInfo__get, __set, __isset, __unset__call e __callStatic__clone e clonazione degli oggetti__serialize e __unserializeRiepilogo - Collezione personalizzata11Sistema dei tipi e gestione degli errori
Dichiarazioni di tipoTipi nullableTipi unione e intersezioneClassi di eccezioneGerarchia di eccezioni personalizzataTry, Catch, FinallyRiepilogo - Validatore di moduli14Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente3Proprietà delle classi
Proprietà di istanza e staticheCostanti nelle classiMetodi e proprietà staticiProprietà private e protectedProprietà readonly (PHP 8.1)Riepilogo - Gestore di conti bancari6Polimorfismo
Ridefinizione dei metodi, ripresaPolimorfismo tramite interfacceSuggerimenti sui tipi e tipi UnionBinding statico tardivoRiepilogo - Processore di pagamenti9Tratti
Introduzione ai trattiUtilizzare più trattiRisoluzione dei conflitti tra trattiMetodi astratti nei trattiTratti vs ereditarietà12Design pattern - Parte 1
Introduzione ai design patternPattern SingletonPattern FactoryPattern ObserverPattern StrategyEsercitati da solo: Compilatore PHP online