Menu
Coddy logo textTech

Patrón State

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy. Lección 79 de 91.

El patrón State es un patrón de diseño de comportamiento que permite que un objeto cambie su comportamiento cuando cambia su estado interno. En lugar de usar condicionales complejas para gestionar diferentes estados, encapsulas cada estado en su propia clase y delegas el comportamiento al objeto de estado actual.

Piensa en una máquina expendedora. Se comporta de manera diferente según si está esperando dinero, ha recibido el pago o está dispensando un producto.

Cada estado tiene sus propias reglas sobre qué acciones están permitidas. El patrón State modela esto creando una clase para cada estado.

<?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 clase de contexto mantiene una referencia al estado actual y delega las llamadas en él:

<?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();

Salida:

Pending
Order moved to processing
Processing
Order has been shipped
Shipped

El patrón State elimina las extensas sentencias switch y hace que añadir nuevos estados sea sencillo. Cada clase de estado es responsable de saber cuál es el estado siguiente, lo que mantiene la lógica organizada y fácil de mantener.

challenge icon

Desafío

Fácil

Construyamos un sistema de flujo de trabajo de documentos utilizando el patrón State. Los documentos de un sistema de gestión de contenido pasan por diferentes etapas: borrador, revisión y publicación; cada etapa tiene sus propias reglas sobre qué acciones están permitidas y qué sucede cuando intentas avanzar el documento.

Organizarás tu código en cuatro archivos:

  • DocumentState.php: Define una interfaz DocumentState que establezca el contrato para todos los estados de los documentos. Debe tener dos métodos: publish(Document $document): string para intentar avanzar el documento y getStatus(): string para devolver el nombre del estado actual.
  • States.php: Incluye la interfaz DocumentState y crea tres clases de estado que la implementen:
    • DraftState: Cuando se llama a publish(), transiciona el documento a ReviewState y devuelve "Document sent for review". Su estado es "Draft".
    • ReviewState: Cuando se llama a publish(), transiciona el documento a PublishedState y devuelve "Document published". Su estado es "Under Review".
    • PublishedState: Cuando se llama a publish(), devuelve "Document is already published" sin cambiar el estado. Su estado es "Published".
  • Document.php: Incluye el archivo States y crea una clase Document que actúe como contexto. Tu documento debe:
    • Aceptar un título en su constructor y comenzar en DraftState
    • Tener un método setState(DocumentState $state): void para cambiar el estado actual
    • Tener un método publish(): string que delegue en el método publish() del estado actual
    • Tener un método getStatus(): string que devuelva el estado del estado actual
  • main.php: Incluye el archivo Document. Recibirás una entrada: el número de veces que se debe llamar a publish() en el documento.

    Crea un Document con el título "My Article". Primero, imprime el estado inicial. Después, llama a publish() el número de veces especificado, imprimiendo el resultado de cada llamada en una línea nueva. Finalmente, imprime el estado final.

    Formato de salida:

    Status: [initial status]
    [publish result 1]
    [publish result 2]
    ...
    Status: [final status]

Cada clase de estado sabe cuál es el siguiente estado en el flujo de trabajo, lo que mantiene la lógica de transición organizada y fácil de modificar. Añadir un nuevo estado como "Archived" simplemente implicaría crear una nueva clase y actualizar las transiciones pertinentes.

Pruébalo tú mismo

<?php

require_once 'Document.php';

// Lee la entrada - número de veces para llamar a publish()
$n = intval(trim(fgets(STDIN)));

// TODO: Crea un Document con el título "My Article"

// TODO: Imprime el estado inicial en el formato: "Status: [status]"

// TODO: Llama a publish() n veces, imprimiendo cada resultado en una nueva línea

// TODO: Imprime el estado final en el formato: "Status: [status]"

?>
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de PHP online