Pular para o conteúdo
GoF Padrões de Projeto Comportamentais
Comportamental escopo de classe
GoF, p. 243

Interpretador

Interpreter

Cada regra da gramática vira uma classe que sabe se avaliar.

Complexidade alta Frequência de uso rara
Intençãocomo o livro define

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.

Analogiauma imagem do mundo real

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.

O problema

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.

A solução

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.

Sintomascomo reconhecer no seu código
  • Regras de negócio configuráveis codificadas como strings avaliadas com eval.
  • Um if gigante que muda toda semana por pedido de marketing.

Estrutura

monta a árvorefilhos ↺ClientContexto«interface»Expressao+ interpretar(ctx)LiteralVariavelE / OU / NAO- esq, dir
Fig. 15 — Interpreter herda / implementa cria usa / contém
Ver este diagrama sendo desenhado, traço a traço
Participantes
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

Listagem 15 Motor de regras de desconto configurável pelo time de marketing
// ── 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.
Na práticaonde ele já existe
  • java.util.regex.Pattern
  • java.text.Format
  • Expressões de filtro em query builders; JSONLogic; SpEL do Spring

Consequências

A favor
  • 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.
Contra
  • 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.
Use quando
  • A gramática é simples e estável.
  • Usuários finais precisam compor regras sem novo deploy.
  • Eficiência não é crítica.
Evite quando
  • A linguagem é complexa (use ANTLR, PEG.js, um parser de verdade).

Relações com outros padrões

costuma andar junto
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

Questãoa resposta está marcada

Quando Interpreter é a escolha ERRADA?

  1. Quando a gramática é complexa — o número de classes explode e um parser generator é melhor
  2. Quando as regras precisam mudar em tempo de execução
  3. Quando os usuários finais escrevem as regras
  4. 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.