Menu
Coddy logo textTech

Padrão State

Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy. Lição 79 de 91.

O Padrão de Estado é um padrão de projeto comportamental que permite que um objeto altere seu comportamento quando seu estado interno muda. Em vez de usar condicionais complexas para lidar com diferentes estados, você encapsula cada estado em sua própria classe e delega o comportamento ao objeto de estado atual.

Pense em uma máquina de venda automática. Ela se comporta de maneira diferente dependendo de estar aguardando dinheiro, ter recebido o pagamento ou estar liberando um produto.

Cada estado tem suas próprias regras sobre quais ações são permitidas. O padrão State modela isso criando uma classe 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";
    }
}

A classe de contexto mantém uma referência ao estado atual e delega as chamadas a ele:

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

Saída:

Pending
Order moved to processing
Processing
Order has been shipped
Shipped

O Padrão State elimina instruções switch extensas e torna simples adicionar novos estados. Cada classe de estado é responsável por saber qual estado vem a seguir, mantendo a lógica organizada e fácil de manter.

challenge icon

Desafio

Fácil

Vamos criar um sistema de fluxo de trabalho de documentos usando o Padrão State. Os documentos em um sistema de gerenciamento de conteúdo passam por diferentes etapas — rascunho, revisão e publicação —, e cada etapa tem suas próprias regras sobre quais ações são permitidas e o que acontece quando você tenta avançar o documento.

Você organizará seu código em quatro arquivos:

  • DocumentState.php: Defina uma interface DocumentState que estabeleça o contrato para todos os estados do documento. Ela deve ter dois métodos: publish(Document $document): string para tentar avançar o documento e getStatus(): string para retornar o nome do estado atual.
  • States.php: Inclua a interface DocumentState e crie três classes de estado que a implementem:
    • DraftState: Quando publish() for chamado, ele fará a transição do documento para ReviewState e retornará "Document sent for review". Seu status é "Draft".
    • ReviewState: Quando publish() for chamado, ele fará a transição do documento para PublishedState e retornará "Document published". Seu status é "Under Review".
    • PublishedState: Quando publish() for chamado, ele retornará "Document is already published" sem alterar o estado. Seu status é "Published".
  • Document.php: Inclua o arquivo States e crie uma classe Document que atue como o contexto. Seu documento deve:
    • Aceitar um título em seu construtor e começar em DraftState
    • Ter um método setState(DocumentState $state): void para alterar o estado atual
    • Ter um método publish(): string que delegue ao método publish() do estado atual
    • Ter um método getStatus(): string que retorne o status do estado atual
  • main.php: Inclua o arquivo Document. Você receberá uma entrada: o número de vezes que deverá chamar publish() no documento.

    Crie um Document com o título "My Article". Primeiro, imprima o status inicial. Em seguida, chame publish() o número de vezes especificado, imprimindo o resultado de cada chamada em uma nova linha. Por fim, imprima o status final.

    Formato da saída:

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

Cada classe de estado sabe qual estado vem a seguir no fluxo de trabalho, mantendo a lógica de transição organizada e fácil de modificar. Adicionar um novo estado, como "Archived", significaria simplesmente criar uma nova classe e atualizar as transições relevantes.

Experimente você mesmo

<?php

require_once 'Document.php';

// Ler a entrada - número de vezes para chamar publish()
$n = intval(trim(fgets(STDIN)));

// TODO: Criar um Document com o título "My Article"

// TODO: Imprimir o status inicial no formato: "Status: [status]"

// TODO: Chamar publish() n vezes, imprimindo cada resultado em uma nova linha

// TODO: Imprimir o status final no formato: "Status: [status]"

?>
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de PHP online