Command
Intenção (GoF)
Seção intitulada “Intenção (GoF)”Encapsular uma requisição como um objeto, permitindo parametrizar clientes com diferentes requisições, enfileirar ou registrar requisições e suportar operações que podem ser desfeitas.
Problema
Seção intitulada “Problema”O invocador de uma operação (um botão, um item de menu, um job) está diretamente acoplado ao código que a executa. Isso impede tratar operações como dados: não dá para enfileirá-las, agendá-las, registrá-las, repeti-las ou desfazê-las de forma uniforme. Sempre que se quer transformar “uma ação” em algo manipulável, o Command entra.
Estrutura
Seção intitulada “Estrutura”- Command: interface com
execute()(e opcionalmenteundo()). - ConcreteCommand: liga uma ação a um Receiver e guarda os parâmetros; implementa
executedelegando ao receiver. - Receiver: quem realmente sabe fazer o trabalho.
- Invoker: dispara o comando sem conhecer o receiver nem a ação concreta.
- Client: cria o ConcreteCommand e configura seu receiver.
Consequências e trade-offs
Seção intitulada “Consequências e trade-offs”Prós
- Desacopla invoker de receiver.
- Operações viram objetos de primeira classe: fila, log, agendamento, macro.
- Undo/redo naturais quando o comando guarda o estado necessário.
- Fácil compor (macro command) e estender novos comandos sem alterar o invoker (OCP).
Contras
- Proliferação de classes pequenas.
- Undo correto pode exigir capturar estado (frequentemente combinado com Memento).
- Indireção adicional entre pedido e execução.
Quando NÃO usar
Seção intitulada “Quando NÃO usar”Para chamadas diretas e simples sem necessidade de fila, log, desfazer ou parametrização. Se não há benefício de “reificar” a ação, uma chamada de método basta.
Exemplo em Java
Seção intitulada “Exemplo em Java”Antes: invoker acoplado à ação
Seção intitulada “Antes: invoker acoplado à ação”public class Editor {
private final Document document;
public Editor(Document document) { this.document = document; }
public void onKey(char c) { document.insert(c); }}Sem forma de desfazer, enfileirar ou registrar o que foi digitado.
Depois: ações como comandos
Seção intitulada “Depois: ações como comandos”public interface Command { void execute(); void undo();}
public class Document { private final StringBuilder text = new StringBuilder();
public void insert(String s) { text.append(s); } public void deleteLast(int n) { text.setLength(Math.max(0, text.length() - n)); } public String content() { return text.toString(); }}
public class InsertTextCommand implements Command {
private final Document document; private final String text;
public InsertTextCommand(Document document, String text) { this.document = document; this.text = text; }
public void execute() { document.insert(text); }
public void undo() { document.deleteLast(text.length()); }}Invoker com histórico de undo/redo:
public class CommandInvoker {
private final Deque<Command> undoStack = new ArrayDeque<>(); private final Deque<Command> redoStack = new ArrayDeque<>();
public void run(Command command) { command.execute(); undoStack.push(command); redoStack.clear(); }
public void undo() { if (undoStack.isEmpty()) return; Command c = undoStack.pop(); c.undo(); redoStack.push(c); }
public void redo() { if (redoStack.isEmpty()) return; Command c = redoStack.pop(); c.execute(); undoStack.push(c); }}Uso:
Document doc = new Document();CommandInvoker invoker = new CommandInvoker();
invoker.run(new InsertTextCommand(doc, "Ola"));invoker.run(new InsertTextCommand(doc, " mundo"));invoker.undo();System.out.println(doc.content());O invoker manipula apenas Command; enfileirar, logar ou serializar comandos passa a ser trivial.
Diagrama
Seção intitulada “Diagrama”sequenceDiagram
participant Client
participant Invoker as CommandInvoker
participant Cmd as InsertTextCommand
participant Receiver as Document
Client->>Cmd: new(document, "Ola")
Client->>Invoker: run(cmd)
Invoker->>Cmd: execute()
Cmd->>Receiver: insert("Ola")
Invoker->>Invoker: undoStack.push(cmd)
Client->>Invoker: undo()
Invoker->>Cmd: undo()
Cmd->>Receiver: deleteLast(3)
Command vs. Caso de Uso
Seção intitulada “Command vs. Caso de Uso”É comum confundir o Command (padrão GoF) com o conceito de caso de uso (Use Cases) de arquiteturas limpas/hexagonais. Há semelhança superficial — ambos encapsulam “uma operação” — mas o propósito difere:
- Command (GoF): mecanismo técnico para tratar uma ação como objeto reificado; foco em undo, fila, log, agendamento, desacoplamento invoker/receiver. É um padrão de implementação.
- Use Case: unidade de comportamento de aplicação que orquestra regras de domínio, entrada/saída e portas (Ports) na Arquitetura Hexagonal. É um conceito de arquitetura, orientado à intenção de negócio.
Eles podem coexistir: um caso de uso pode ser implementado com objetos de comando (por exemplo, um CommandBus que despacha CreateOrderCommand para um handler). Nesse estilo (CQRS), o “command” carrega a intenção de negócio, mas o padrão que o move pela infraestrutura ainda é o Command do GoF. Não são sinônimos: um é técnica, o outro é fronteira arquitetural.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”undo()que não restaura o estado real por não ter capturado dados suficientes na execução — combine com Memento quando o inverso não for calculável.- Comando anêmico que vira só um wrapper trivial de uma chamada, sem nenhum dos benefícios (fila/log/undo). Se não há ganho, não reifique.
- Command com lógica de negócio pesada embutida, transformando-o num God Object; ele deve delegar ao receiver.
- Confundir Command com Use Case e derramar orquestração de aplicação dentro de um padrão de infraestrutura.
Relações
Seção intitulada “Relações”- Padrão comportamental de Padrões Comportamentais / Design Patterns (GoF).
- Combina com Chain of Responsibility (handlers que disparam comandos) e com Memento (undo por snapshot).
- Contrasta e coopera com Use Cases na Arquitetura Hexagonal e Ports.
- Apoia o OCP ao adicionar novos comandos sem tocar no invoker.
- Índice: Design Patterns (GoF) · Catálogo de Patterns.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Que capacidades surgem ao encapsular uma requisição como objeto?
Resposta
Parametrizar clientes com diferentes ações, enfileirar, registrar em log, agendar, compor em macros e principalmente desfazer/refazer — tudo porque a operação passa a ser um dado manipulável, não uma chamada fixa.
2. Qual o papel do Invoker e por que ele não conhece o Receiver?
Resposta
O Invoker apenas dispara execute() (e gerencia histórico para undo/redo) sobre a abstração Command. Ele não conhece o Receiver porque cada ConcreteCommand encapsula a ligação com seu receiver, desacoplando quem pede de quem executa.
3. O que o Command precisa para suportar undo confiável quando a operação não é reversível por cálculo?
Resposta
Precisa capturar o estado necessário para restaurar a situação anterior. Quando o inverso não é calculável (ex.: sobrescrita de dados), combina-se o Command com o Memento, que guarda um snapshot do estado a ser restaurado no undo().
4. Command (GoF) e Use Case são a mesma coisa?
Resposta
Não. Command é um padrão técnico que reifica uma ação (foco em undo/fila/log/desacoplamento). Use Case é uma unidade arquitetural que orquestra regras de negócio e portas. Podem coexistir — um caso de uso pode ser implementado via CommandBus — mas operam em níveis diferentes: técnica vs. fronteira de aplicação.