GoF, p. 243
Interpretador
Interpreter
Cada regra da gramática vira uma classe que sabe se avaliar.
Dado um idioma, definir uma representação para sua gramática juntamente com um interpretador que usa a representação para interpretar sentenças do idioma.
A partitura musical
Uma partitura é uma linguagem: símbolos com regras de composição. O músico “interpreta” recursivamente — o compasso é feito de notas, a peça é feita de compassos. Cada símbolo sabe o que fazer consigo mesmo, e o resultado emerge da composição.
Você tem um domínio onde os usuários precisam expressar regras: filtros de busca, fórmulas de planilha, condições de desconto, permissões. Codificar cada combinação em `if` é impossível — as combinações são infinitas.
Modele a gramática como uma árvore de objetos. Cada regra vira uma classe com um método `interpretar(contexto)`. Expressões terminais avaliam a si mesmas; expressões não-terminais combinam os resultados dos filhos. A árvore montada é a sentença.
- Regras de negócio configuráveis codificadas como strings avaliadas com
eval. - Um
ifgigante que muda toda semana por pedido de marketing.
Estrutura
- AbstractExpression
- Declara `interpretar(contexto)`.
- TerminalExpression
- Folhas da gramática: literais, variáveis.
- NonterminalExpression
- Regras compostas: E, OU, NÃO, soma, comparação.
- Context
- Informação global da interpretação (valores das variáveis).
- Client
- Monta a árvore sintática e dispara `interpretar()`.
Implementação
// ── Expressão abstrata ────────────────────────────────
class Expressao { interpretar(ctx) { throw new Error('abstrato'); } }
// ── Terminais: sabem se avaliar sozinhos ──────────────
class Literal extends Expressao {
constructor(v) { super(); this.v = v; }
interpretar() { return this.v; }
}
class Campo extends Expressao {
constructor(nome) { super(); this.nome = nome; }
interpretar(ctx) { return ctx[this.nome]; }
}
// ── Não-terminais: combinam os filhos, recursivamente ──
class Maior extends Expressao {
constructor(a, b) { super(); this.a = a; this.b = b; }
interpretar(ctx) { return this.a.interpretar(ctx) > this.b.interpretar(ctx); }
}
class Igual extends Expressao {
constructor(a, b) { super(); this.a = a; this.b = b; }
interpretar(ctx) { return this.a.interpretar(ctx) === this.b.interpretar(ctx); }
}
class E extends Expressao {
constructor(...partes) { super(); this.partes = partes; }
interpretar(ctx) { return this.partes.every(p => p.interpretar(ctx)); }
}
class Ou extends Expressao {
constructor(...partes) { super(); this.partes = partes; }
interpretar(ctx) { return this.partes.some(p => p.interpretar(ctx)); }
}
// ── A regra: "(total > 500 E uf = SP) OU cliente = vip" ──
const regra = new Ou(
new E(new Maior(new Campo('total'), new Literal(500)),
new Igual(new Campo('uf'), new Literal('SP'))),
new Igual(new Campo('plano'), new Literal('vip')),
);
console.log(regra.interpretar({ total: 800, uf: 'SP', plano: 'free' })); // true
console.log(regra.interpretar({ total: 100, uf: 'RJ', plano: 'vip' })); // true
console.log(regra.interpretar({ total: 100, uf: 'RJ', plano: 'free' })); // false
// A árvore pode vir de um JSON salvo no banco — regras mudam sem deploy. - java.util.regex.Pattern
- java.text.Format
- Expressões de filtro em query builders; JSONLogic; SpEL do Spring
Consequências
- Fácil mudar e estender a gramática: cada regra é uma classe.
- Regras podem ser montadas em tempo de execução, vindas de configuração ou banco.
- Implementar a gramática fica trivial quando ela é simples.
- Gramáticas complexas geram um número enorme de classes — aí use um parser generator.
- É o padrão menos usado do catálogo; muita gente nunca precisará dele.
- Desempenho pode ser ruim comparado a um interpretador dedicado.
- A gramática é simples e estável.
- Usuários finais precisam compor regras sem novo deploy.
- Eficiência não é crítica.
- A linguagem é complexa (use ANTLR, PEG.js, um parser de verdade).
Relações com outros padrões
- Composite
- A árvore sintática É um Composite — Interpreter é uma aplicação especializada dele.
- Visitor
- Visitor permite adicionar operações (otimizar, imprimir, tipar) sobre a árvore sem tocar nos nós.
- Iterator
- Percorrer a árvore sintática.
- Flyweight
- Símbolos terminais repetidos podem ser compartilhados.
Verificação
Quando Interpreter é a escolha ERRADA?
- Quando a gramática é complexa — o número de classes explode e um parser generator é melhor
- Quando as regras precisam mudar em tempo de execução
- Quando os usuários finais escrevem as regras
- Quando a gramática tem operadores lógicos
O padrão brilha em mini-linguagens simples. Para linguagens reais, o custo de manter uma classe por regra gramatical é proibitivo.