🤝 Capítulo 15: Comportamentais III: Command e Memento
Módulo 10: Paradigmas de Programação & Padrões de Projeto • Bloco 4: Padrões GoF Comportamentais
🏛️ Transações Reversíveis, Filas e Snapshots de Memória: Como transformar operações computacionais voláteis em objetos duráveis que podem ser enfileirados, serializados, auditados e desfeitos? Neste capítulo, exploramos dois padrões complementares do Gang of Four que fundamentam os sistemas transacionais modernos: o Command (que encapsula a ação a ser executada) e o Memento (que encapsula o estado a ser restaurado sem quebrar o encapsulamento).
🗺️ Mapa Conceitual do Capítulo
flowchart TD
COMPORT3["Padrões Comportamentais (Transações & Snapshots)"]
COMPORT3 --> CMD["1. Command (Comando / Ação Encapsulada)<br/>• Transforma uma requisição em um objeto de 1ª classe<br/>• Desacopla Invoker do Receiver<br/>• Viabiliza filas assíncronas, macros e pilhas de Undo/Redo"]
COMPORT3 --> MEM["2. Memento (Lembrança / Snapshot Imutável)<br/>• Captura o estado interno sem expor atributos privados<br/>• Originator (gera/restaura) vs Caretaker (armazena)<br/>• Checkpoints, Rollbacks e Delta Compression"]
COMPORT3 --> SYN["3. Sinergia Command + Memento<br/>• O Command armazena o Memento antes de executar<br/>• O método undo() restaura o memento com custo determinístico"]
style COMPORT3 fill:#fce4ec,stroke:#c2185b,stroke-width:2px
style CMD fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px
style MEM fill:#e1f5fe,stroke:#03a9f4,stroke-width:1.5px
style SYN fill:#fffde7,stroke:#fbc02d,stroke-width:1.5px
🕹️ 1. O Padrão Command: Ações como Cidadãos de Primeira Classe
“Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.” — GoF (1994)
1.1 A Dissociação entre Gatilho e Execução
Na programação procedural ou orientada a eventos tradicional, o botão de uma interface gráfica chama diretamente o método da regra de negócio: btnSalvar.onClick = banco.salvarUsuario().
Isso gera problemas imediatos:
- Impossível reutilizar o mesmo comando em um atalho de teclado (
Ctrl+S), em um menu de contexto e em um botão da barra de ferramentas sem duplicar código; - Impossível implementar operações de “Desfazer” (Undo) e “Refazer” (Redo);
- Impossível colocar a requisição em uma fila em segundo plano para execução agendada.
O padrão Command soluciona isso interpondo um objeto intermediário que implementa uma interface contratual com método executar() e, opcionalmente, desfazer():
classDiagram
class Invocador {
-historico: List~ICommand~
+definirComando(ICommand) void
+acionar() void
+desfazer() void
}
class ICommand {
<<interface>>
+executar() void
+desfazer() void
}
class ConcreteCommand {
-receptor: ServicoFinanceiroReceiver
-parametros: DadosTransacao
+executar() void
+desfazer() void
}
class ServicoFinanceiroReceiver {
+creditar(conta, valor) void
+debitar(conta, valor) void
}
Invocador o--> ICommand : Dispara
ICommand <|.. ConcreteCommand
ConcreteCommand --> ServicoFinanceiroReceiver : Delega ação real
1.2 Os Quatro Grandes Casos de Uso do Command na Indústria
- Pilhas de Desfazer e Refazer (Undo/Redo): O invocador gerencia duas pilhas (
UndoStackeRedoStack). Ao executar, empilha o comando executado; ao clicar em “Desfazer”, desempilha e chamacmd.desfazer(). - Filas de Tarefas Assíncronas (Background Job Queues): Em sistemas modernos com Celery (Python), Sidekiq (Ruby) ou Hangfire (C#), uma requisição web cria um objeto
ProcessarVideoCommand, serializa-o em JSON e o joga no Redis. Trabalhadores em segundo plano (Workers) desserializam e invocam.executar(). - Write-Ahead Logging (WAL) para Tolerância a Falhas: Bancos de dados e brokers gravam cada comando em um log sequencial em disco antes de aplicar na memória. Se a energia cair, o banco lê o log e reexecuta os comandos (Replay).
- Macro-Comandos (Composite Commands): Um único comando que agrupa uma sequência de dezenas de outros comandos subordinados executados em lote de forma transacional.
💾 2. O Padrão Memento: Snapshots sem Quebra de Encapsulamento
“Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.” — GoF (1994)
2.1 Por que Métodos Get/Set Públicos Destroem o Design?
Se uma classe EditorDocumento precisa que seu estado seja salvo periodicamente, a solução ingênua seria expor todos os seus campos privados via getters públicos para que um gerenciador de histórico salve em uma lista.
Isso viola fatalmente o encapsulamento:
- Qualquer classe externa passa a ter acesso aos detalhes internos de implementação do documento;
- Se a estrutura interna mudar (ex.: de string para árvore de nós de formatação), o gerenciador de histórico quebra.
2.2 O Segredo do Memento: Interfaces Estreita e Ampla
O padrão divide as responsabilidades em três participantes rigorosos:
- Originador (Originator): O objeto de negócio cujo estado precisa ser salvo. Ele cria novos mementos contendo seu estado atual (
criar_memento()) e sabe como restaurar seu estado interno a partir de um memento (restaurar(memento)). - Memento: O objeto imutável que encapsula a foto instantânea (snapshot) dos dados. Ele possui uma Interface Estreita (Narrow Interface) para o mundo exterior (não expõe nenhum método de leitura ou modificação) e uma Interface Ampla (Wide Interface) acessível exclusivamente pelo Originador.
- Guardião (Caretaker): O repositório histórico (ex.: gerenciador de checkpoints ou transações). Ele apenas guarda e devolve mementos opacos, sem jamais ler, inspecionar ou tentar alterar seu conteúdo interno.
sequenceDiagram
autonumber
actor Cliente
participant O as Originador (EditorTexto)
participant C as Caretaker (GerenciadorCheckpoints)
participant M as Memento (Snapshot Imutável)
Cliente->>O: digitar("Capítulo 15")
Cliente->>O: criarCheckpoint()
O->>M: new Memento("Capítulo 15")
O->>C: guardarMemento(M)
Note over C: O Caretaker guarda o Memento<br/>como uma caixa-preta opaca
Cliente->>O: digitar(" - Erro de digitação!")
Note over Cliente: O usuário solicita desfazer
Cliente->>C: obterUltimoMemento()
C-->>Cliente: M
Cliente->>O: restaurar(M)
O->>M: extrairEstadoInterno()
Note over O: Estado restaurado para: "Capítulo 15"
⚡ 3. Sinergia Arquitetural: Command + Memento
Quando combinados, esses dois padrões formam a fundação definitiva de sistemas transacionais e de computação resiliente:
- Cada comando que altera o modelo de domínio captura um
MementodoOriginatorimediatamente antes de aplicar a alteração:def executar(self): self._memento_anterior = self._origem.criar_memento() self._origem.modificar(...) def desfazer(self): self._origem.restaurar(self._memento_anterior) - Isso elimina a necessidade de escrever algoritmos matemáticos complexos e propensos a erros para “reverter” operações: o estado simplesmente retorna à foto exata tirada momentos antes.
📊 4. Matriz Comparativa Sistemática: Command vs. Memento vs. Strategy
| Dimensão de Análise | Command | Memento | Strategy |
|---|---|---|---|
| Foco Central | Ação / Intenção (o que fazer) | Estado / Snapshot (o que o objeto é) | Algoritmo (como calcular) |
| Temporalidade | Orientado ao futuro (ações pendentes, filas) | Orientado ao passado (histórico, pontos de restauração) | Orientado ao presente (troca dinâmica imediata) |
| Reversibilidade | Implementa método formal desfazer() |
Fornece a imagem de memória para a restauração | Geralmente irreversível por si só |
| Encapsulamento | Conhece o receptor e aciona seus métodos | Oculta dados internos através de interface opaca | Recebe dados do contexto via parâmetros |