Software · Design Patterns

Padrões Comportamentais Restantes: Command, Iterator e Interpreter

Como transformar ações em objetos manipuláveis com Command, padronizar travessias agnósticas de coleções com Iterator e interpretar linguagens de domínio com Interpreter no catálogo clássico do GoF.

Diagrama conceitual de padrões comportamentais GoF demonstrando pilhas de execução Command, cursores de travessia Iterator e árvores de sintaxe Interpreter
Neste artigo

Padrões comportamentais organizam a comunicação, a atribuição de responsabilidades e os fluxos de controle entre objetos em execução. Enquanto padrões como Mediator e Observer focam em desatar nós de comunicação entre componentes desacoplados, outros três padrões do catálogo GoF abordam operações estruturadas sobre ações e dados: Command, Iterator e Interpreter.

Esses três padrões transformam operações efêmeras em construções de primeira classe na linguagem, permitindo enfileirar ações, desfazer comandos, percorrer coleções complexas sem expor suas estruturas internas e avaliar linguagens específicas de domínio (DSLs).

flowchart TD
    subgraph CommandFlow["Command Pattern"]
        Invoker["Invocador (Botão / Fila)"] -->|executa| Cmd["ICommand.Execute()"]
        Cmd -->|modifica| Receiver["Receptor de Negócio"]
        Cmd -.->|suporta| Undo["ICommand.Undo()"]
    end

    subgraph IteratorFlow["Iterator Pattern"]
        Collection["Coleção Oculta"] --> Iterator["Iterator (Cursor)"]
        Iterator -->|MoveNext / Current| Consumer["Consumidor"]
    end

Comparativo de Intenções

Padrão Problema Resolvido Estrutura Central Exemplo no Ecossistema Moderno
Command Encapsula uma solicitação como um objeto independente Interface com métodos Execute() e opcionalmente Undo() Filas de tarefas em segundo plano, botões de interface, arquiteturas CQRS
Iterator Percorre elementos de um agregado sem violar o encapsulamento Abstração de cursor com controle de avanço e leitura IEnumerable no C#, iteradores em Python, Symbol.iterator no JS
Interpreter Avalia sentenças em uma linguagem ou gramática formal Árvore sintática abstrata (AST) com nós terminais e não terminais Parsers de fórmulas matemáticas, validadores de filtros e regras de negócio

1. Command: Ações como Entidades de Primeira Classe

Ao transformar uma solicitação em um objeto isolado, o padrão Command desacopla o emissor da ação daquele que sabe como executá-la. Esse encapsulamento habilita quatro capacidades arquiteturais imediatas:

  1. Parâmetros e Agendamento: Comandos podem ser serializados, armazenados em tabelas de auditoria e agendados para execução futura.
  2. Histórico e Desfazer (Undo / Redo): Guardando uma pilha de comandos executados, a aplicação pode reverter operações aplicando o inverso de cada comando.
  3. Resiliência e Retries: Em caso de falha de rede temporária, o comando pode ser reinserido em uma fila de mensageria com todas as suas variáveis preservadas.
  4. Base para CQRS: A segregação de comandos e consultas deriva diretamente dessa separação, onde Commands representam intenções explícitas de mutação de estado.
public interface ICommand 
{
    Task ExecuteAsync();
    Task UndoAsync();
}

public class TransferFundsCommand : ICommand 
{
    private readonly Account _origin;
    private readonly Account _destination;
    private readonly decimal _amount;

    public TransferFundsCommand(Account origin, Account destination, decimal amount) 
    {
        _origin = origin;
        _destination = destination;
        _amount = amount;
    }

    public async Task ExecuteAsync() 
    {
        _origin.Withdraw(_amount);
        _destination.Deposit(_amount);
    }

    public async Task UndoAsync() 
    {
        _destination.Withdraw(_amount);
        _origin.Deposit(_amount);
    }
}

2. Iterator: Travessia Agnóstica e Avaliação Sob Demanda

Antes da popularização do padrão Iterator, iterar sobre estruturas de dados exigia conhecer os detalhes íntimos da coleção: um array exigia índices inteiros, enquanto uma lista duplamente ligada exigia ponteiros para nós adjacentes.

O Iterator resolve isso expondo uma interface uniforme que fornece acesso sequencial aos elementos de um objeto agregado sem expor sua representação interna.

Nas linguagens modernas, o padrão é tão essencial que foi incorporado diretamente na sintaxe dos compiladores. No C#, construções com a palavra-chave yield return geram automaticamente uma máquina de estados que implementa IEnumerator<T>:

public class PagedDataStream 
{
    public static async IAsyncEnumerable<string> StreamAuditLogsAsync(ApiClient client) 
    {
        int page = 1;
        bool hasMore = true;

        while (hasMore) 
        {
            var logs = await client.FetchPageAsync(page++);
            if (logs.Count == 0) yield break;

            foreach (var log in logs) 
            {
                yield return log; // Avaliação sob demanda (streaming de memória)
            }
        }
    }
}

O consumidor do método consome os dados usando um simples await foreach, sem alocar a lista inteira na memória de uma só vez e sem precisar controlar índices de paginação manualmente.

3. Interpreter: Construindo Interpretadores de Regras

O padrão Interpreter define uma representação gramatical para uma linguagem e um interpretador que utiliza essa representação para avaliar sentenças. Ele é indicado quando um domínio de negócio possui problemas repetitivos que podem ser expressos como sentenças em uma mini-linguagem.

classDiagram
    class IExpression {
        <<interface>>
        +Interpret(Context context) int
    }
    class NumberExpression {
        -int _number
        +Interpret(Context context) int
    }
    class AddExpression {
        -IExpression _left
        -IExpression _right
        +Interpret(Context context) int
    }

    IExpression <|.. NumberExpression
    IExpression <|.. AddExpression
    AddExpression --> IExpression : compõe filhos

Considere a avaliação de regras booleanas dinâmicas fornecidas por usuários (por exemplo: Idade > 18 AND Renda > 5000):

public interface IBooleanExpression 
{
    bool Evaluate(UserContext context);
}

// Nó Terminal (Verificação pontual)
public class AgeGreaterThanExpression : IBooleanExpression 
{
    private readonly int _minAge;
    public AgeGreaterThanExpression(int minAge) => _minAge = minAge;

    public bool Evaluate(UserContext context) => context.Age > _minAge;
}

// Nó Não-Terminal (Operador Lógico Composto)
public class AndExpression : IBooleanExpression 
{
    private readonly IBooleanExpression _left;
    private readonly IBooleanExpression _right;

    public AndExpression(IBooleanExpression left, IBooleanExpression right) 
    {
        _left = left;
        _right = right;
    }

    public bool Evaluate(UserContext context) => 
        _left.Evaluate(context) && _right.Evaluate(context);
}

Ao montar uma árvore de nós sintáticos combinando expressões terminais e operadores lógicos, o motor avalia regras complexas sem depender de bibliotecas externas pesadas e com segurança total contra execução de código arbitrário.

Explorar este tema no Grafo de Conhecimento