Pular para o conteúdo
GoF/23 Padrões de Projeto Comportamentais
243
Comportamental escopo de classe

Interpreter

Interpretador

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

Complexidade alta Frequência de uso rara
D

Definição

Intenção — palavra por palavra, como 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.

E

Exemplo e motivação

Analogia

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.
Na práticaonde ele já existe
  • java.util.regex.Pattern
  • java.text.Format
  • Expressões de filtro em query builders; JSONLogic; SpEL do Spring
D

Diagrama

ClientContexto«interface»Expressao+ interpretar(ctx)LiteralVariavelE / OU / NAO- esq, dirmonta a árvorefilhos ↺
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()`.
A

Aplicabilidade

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).
C

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.
Apêndice

Código

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.
Apêndice

Relaçõ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.
Apêndice

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.