Tecnologia além do código.
Régys Borges da Silveira

Design Patterns em Delphi 13: State e Command – Controle de Fluxo e Encapsulamento

Neste artigo 16 tópicos

Você já se deparou com uma unit de negócio onde o comportamento de um método é governado por um emaranhado de if FStatus = stPendente then... else if FStatus = stPago then...? Ou talvez tenha tido dificuldade em implementar um sistema de “Desfazer” (Undo) ou uma fila de processamento assíncrono?

Neste artigo, veremos como o padrão State limpa a lógica de estados finitos e como o Command transforma métodos em cidadãos de primeira classe no Delphi 13.

1. State: Objetos com Crise de Identidade (Positiva)

No desenvolvimento de sistemas corporativos, é comum encontrarmos entidades cujo comportamento é ditado por um “status” (Ex: Pedido Aberto, Faturado, Cancelado, Devolvido). O erro clássico é tentar gerenciar as regras de transição e permissões usando blocos massivos de if-then-else ou case dentro da classe de negócio.

O Problema: A Fragilidade dos Estados Mutáveis

Quando a lógica de estado está espalhada, o código torna-se frágil.

  • Cenário de Risco: Você permite que um pedido seja cancelado, mas esquece de verificar se ele já foi faturado em um dos dez pontos de validação.
  • Impacto no Delphi 13: Adicionar um novo estado (como “Em Separação”) exige que você vasculhe toda a Unit em busca de condicionais de status, aumentando drasticamente o risco de bugs de regressão e violando o Princípio Aberto/Fechado (OCP).

A Solução: O Objeto como seu Próprio Comportamento

O padrão State propõe que cada estado seja uma classe independente que implementa uma interface comum (ex: IPedidoEstado). O objeto principal (Contexto) apenas delega as ações para a instância do estado atual.

Por que isso revoluciona sua arquitetura no Delphi 13?

  1. Eliminação de Condicionais: O “cérebro” do objeto é movido para classes menores. Se o estado atual é TEstadoFaturado, o método Cancelar simplesmente lança uma exceção ou executa a lógica de estorno específica, sem precisar de um único if.
  2. Transições Seguras: A lógica de qual estado vem depois de qual fica encapsulada. O estado TAberto sabe que pode evoluir para TPago, mas não para Entregue.
  3. Modularidade Sênior: Você ganha a capacidade de adicionar novos estados criando apenas uma nova Unit e classe, sem tocar no código core da classe de Pedido.

No Delphi 13, a transição entre estados torna-se fluida e atômica. Ao mudar a instância de estado, o objeto altera seu vocabulário de comportamentos instantaneamente, garantindo que a regra de negócio seja respeitada por design, e não por verificações manuais exaustivas.

2. Command: Transformando Ações em Objetos

O padrão Command encapsula uma solicitação como um objeto, permitindo parametrizar clientes com diferentes solicitações, enfileirar ou registrar log de solicitações e suportar operações que podem ser desfeitas.

Implementação Técnica: Desacoplando o “Quem pede” do “Quem faz”

No Delphi 13, o Command é a base para sistemas que precisam de filas de execução ou botões que compartilham a mesma lógica sem estar presos à UI.

unit Regys.Patterns.Command;

interface

type
  // 1. A Interface do Comando
  ICommand = interface
    ['{C1D2E3B4-A5F6-4789-B0C1-D2E3F4A5B6C7}']
    procedure Execute;
    procedure Undo;
  end;

  // 2. O Receiver (Quem realmente sabe fazer o trabalho)
  TLuz = class
  public
    procedure Ligar;
    procedure Desligar;
  end;

  // 3. O Comando Concreto
  TLigarLuzCommand = class(TInterfacedObject, ICommand)
  private
    FLuz: TLuz;
  public
    constructor Create(ALuz: TLuz);
    procedure Execute;
    procedure Undo;
  end;

implementation

{ TLigarLuzCommand }
constructor TLigarLuzCommand.Create(ALuz: TLuz);
begin
  FLuz := ALuz;
end;

procedure TLigarLuzCommand.Execute;
begin
  FLuz.Ligar;
end;

procedure TLigarLuzCommand.Undo;
begin
  FLuz.Desligar;
end;

end.

3. Aplicação no Dia a Dia: Workflows e Sistemas de Undo/Redo

No quotidiano de um arquiteto Delphi 13, a aplicação conjunta de State e Command permite construir sistemas que não apenas “funcionam”, mas que são capazes de gerenciar processos de longa duração e interações ricas com o utilizador.

A. State: Gerenciando o Ciclo de Vida de Documentos Fiscais

Um dos cenários mais complexos em ERPs brasileiros é o fluxo de uma NF-e ou CT-e. O documento passa por estados como: Em Digitação, Validada, Transmitida, Autorizada, Cancelada ou Encerrada.

  • Uso Prático: Cada estado define o que pode acontecer a seguir. No estado Autorizada, o método Editar do State lança uma exceção, enquanto o método Imprimir executa a geração do DANFe.
  • Vantagem: Você centraliza as regras de negócio fiscais dentro dos estados correspondentes. A sua classe TNotaFiscal fica limpa, delegando a inteligência para os objetos de estado, o que facilita enormemente a manutenção quando a legislação impõe novas regras de transição.

B. Command: Sistemas de Undo/Redo e Transações de UI

A implementação de “Desfazer” (Undo) é um diferencial de qualidade em qualquer software. Com o Command, cada ação do utilizador (editar um campo, apagar um registo) é encapsulada.

  • Implementação no Delphi 13: Você mantém uma TStack<ICommand> chamada UndoStack. Quando o utilizador clica em “Desfazer”, você retira o último comando e chama o método Undo.
  • Cenário Real: Em editores de layout ou cadastros complexos, isso evita que o utilizador perca trabalho e permite que o sistema reverta estados do banco de dados ou da memória de forma atômica e segura.

C. Command para Processamento Assíncrono e Filas (Background Tasks)

Com a necessidade de manter a UI sempre responsiva no Delphi 13, tarefas pesadas (como gerar um PDF de 500 páginas ou enviar um lote de e-mails) não podem travar a aplicação.

  • A Transição: Em vez de chamar o método pesado diretamente no botão, você cria um objeto TExportCommand e o coloca numa TThreadedQueue<ICommand>.
  • Vantagem: Uma ou mais threads de background (trabalhando com TTask) consomem esta fila. O padrão Command garante que todos os parâmetros necessários para a execução foram capturados no momento em que o utilizador clicou no botão, evitando erros de concorrência.

D. Automação de Testes e Macros

Ao utilizar o Command, você ganha a capacidade de “gravar” sequências de ações. Isso é extremamente útil para criar testes de integração automatizados: você pode disparar uma lista de comandos pré-configurados e verificar o estado final do sistema, garantindo que o workflow definido pelo padrão State foi respeitado integralmente.

4. Nuances Técnicas: Memória e Transição de Estados no Delphi 13

A implementação dos padrões State e Command no Delphi 13 exige um olhar atento para o ciclo de vida dos objetos. Como ambos lidam com a criação constante de instâncias (novos estados ou novos comandos), a gestão eficiente de recursos é o que separa um protótipo de um sistema de missão crítica.

A. Gestão de Memória com Interfaces e ARC

Tanto no State quanto no Command, o uso de Interfaces (IInterface) é a prática recomendada.

  • No State: Quando o contexto altera o estado atual (FEstado := TEstadoNovo.Create(Self)), a contagem de referência do estado antigo cai para zero, disparando a destruição automática. Isso evita o gerenciamento manual complexo de Free dentro de métodos de transição.
  • No Command: Ao usar uma TStack<ICommand> para Undo, os comandos permanecem na memória. Sem interfaces, você teria que percorrer a pilha manualmente para liberar cada objeto. Com interfaces, basta limpar a lista (FStack.Clear) e o Delphi cuidou do restante.

B. Transição de Estados: Quem manda em quem?

Uma dúvida comum no Delphi 13 é: “Onde deve residir a lógica de troca de estado?”.

  1. No Contexto: Mais simples, mas torna o contexto pesado.
  2. Nos Estados (Recomendado): Cada estado concreto conhece o seu sucessor. Ao finalizar sua lógica, o estado chama um método no contexto para injetar a próxima instância.
  • Dica Técnica: No Delphi 13, passe o contexto como uma referência [Weak] para o estado, ou use uma interface para evitar referências circulares que impediriam a liberação da memória.

C. Command e o Desafio da Thread-Safety

Ao usar o Command para processamento em background (via TTask ou TThreadedQueue), você deve garantir que o comando seja imutável após sua criação.

  • O Perigo: Se um comando acessa um componente da UI (como um TEdit.Text) diretamente de uma thread secundária, o sistema irá travar.
  • A Solução: Capture todos os valores necessários no Constructor do comando. No método Execute, trabalhe apenas com essas variáveis locais. Se precisar atualizar a UI ao final, use TThread.Queue para despachar o resultado de volta à Main Thread.

D. Overhead de Instanciação vs. Flyweight

Se o seu sistema alterna estados milhares de vezes por segundo, criar um novo objeto a cada transição pode sobrecarregar o Memory Manager.

  • Otimização: Em casos de alta performance, você pode combinar o State com o padrão Flyweight, mantendo instâncias estáticas dos estados e apenas trocando a referência no contexto, eliminando o custo de AllocMem e FreeMem.

E. Resumo

  • Gestão de Memória com Interfaces: Tanto no State quanto no Command, o uso de Interfaces no Delphi 13 facilita a vida. No State, quando o Contexto troca de FEstadoAtual := TEstadoNovo.Create(Self), a contagem de referência limpa o estado antigo automaticamente.
  • Transição de Estados: Uma dúvida comum é: “Quem deve trocar o estado?”. No Delphi 13, preferimos que os próprios Estados Concretos decidam para qual estado o Contexto deve ir, passando o Contexto no construtor do Estado.
  • Command e Anonymous Methods: Para comandos muito simples, você pode usar métodos anônimos (TProc). No entanto, para sistemas que exigem Undo, a abordagem baseada em classes e interfaces vista acima é superior por permitir o armazenamento de estados anteriores.

Referências

FLICK, Stefan. Design Patterns with Delphi: Build enterprise-grade applications with modern Delphi and the right design patterns. 1. ed. Birmingham: Packt Publishing, 2024.

GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos. 1. ed. Porto Alegre: Bookman, 2000.

NOGUEIRA, Rodrigo. Delphi e Clean Architecture: Princípios e práticas para software escalável. 2. ed. São Paulo: Editora Engenharia de Software, 2025.

TEIXEIRA, Marcello. Delphi High Performance: Build fast Delphi applications using concurrency, parallel programming and memory management. 1. ed. Birmingham: Packt Publishing, 2018.

Compartilhar por e-mail

0 comentário(s)

Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.

Um próximo passo para cada interesse.

Continue estudando com os livros, acompanhe as videoaulas ou consulte as soluções para o seu projeto.

Descubra mais sobre Régys Borges da Silveira

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo