Menu
Coddy logo textTech

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
Shipped

Il 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.

challenge icon

Sfida

Facile

Costruiamo 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'interfaccia DocumentState che stabilisce il contratto per tutti gli stati del documento. Dovrebbe avere due metodi: publish(Document $document): string per tentare di far avanzare il documento e getStatus(): string per restituire il nome dello stato corrente.
  • States.php: includi l'interfaccia DocumentState e crea tre classi di stato che la implementano:
    • DraftState: quando viene chiamato publish(), il documento passa a ReviewState e il metodo restituisce "Document sent for review". Il suo stato è "Draft".
    • ReviewState: quando viene chiamato publish(), il documento passa a PublishedState e il metodo restituisce "Document published". Il suo stato è "Under Review".
    • PublishedState: quando viene chiamato publish(), il metodo restituisce "Document is already published" senza modificare lo stato. Il suo stato è "Published".
  • Document.php: includi il file States e crea una classe Document che funge da contesto. Il tuo documento dovrà:
    • Accettare un titolo nel costruttore e iniziare nello stato DraftState
    • Avere un metodo setState(DocumentState $state): void per cambiare lo stato corrente
    • Avere un metodo publish(): string che delega al metodo publish() dello stato corrente
    • Avere un metodo getStatus(): string che restituisce lo stato corrente
  • main.php: includi il file Document. Riceverai un input: il numero di volte in cui chiamare publish() sul documento.

    Crea un Document con il titolo "My Article". Per prima cosa, stampa lo stato iniziale. Poi, chiama publish() 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]"

?>
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore PHP online