Il problema: logica configurabile che non può vivere nel codice
Un sistema di sconti ha regole che cambiano ogni settimana: "se il carrello supera 100 EUR e il cliente e premium, applica il 15%". "Se e Black Friday e la categoria e elettronica, applica il 25%". Hardcodare queste regole nel PHP significa deploy per ogni cambio. Metterle in un database come stringhe opache non basta — servono condizioni composte con AND, OR, confronti e variabili.
L'Interpreter Pattern propone di definire un piccolo linguaggio formale per esprimere queste regole e costruire un interprete che le esegue a runtime. La regola diventa un'espressione strutturata: AND(GT(cart.total, 100), EQ(customer.tier, "premium")). L'interprete attraversa questa struttura e la valuta nel contesto corrente.
Cos'e l'Interpreter Pattern: definizione formale
Il Gang of Four definisce l'Interpreter come un pattern comportamentale che "dato un linguaggio, definisce una rappresentazione per la sua grammatica insieme a un interprete che usa la rappresentazione per interpretare frasi nel linguaggio". La struttura:
-
AbstractExpression: interfaccia con
interpret(Context $ctx): mixed - TerminalExpression: espressioni foglia che non contengono sotto-espressioni (variabili, costanti)
- NonterminalExpression: espressioni composte che contengono sotto-espressioni (AND, OR, GT, EQ)
- Context: contiene le variabili e i valori disponibili durante l'interpretazione
Esempio teorico: un motore di regole per sconti
Definiamo le espressioni del nostro mini-linguaggio:
-
VariableExpression('cart.total'): legge il valore dal contesto. Terminal expression. -
LiteralExpression(100): restituisce un valore costante. Terminal expression. -
GreaterThanExpression($left, $right): valuta$left->interpret($ctx) > $right->interpret($ctx). Nonterminal. -
EqualsExpression($left, $right): confronto di uguaglianza. Nonterminal. -
AndExpression($left, $right): entrambe le sotto-espressioni devono essere true. Nonterminal. -
OrExpression($left, $right): almeno una deve essere true. Nonterminal. -
NotExpression($expr): nega il risultato. Nonterminal.
La regola "carrello > 100 AND cliente premium" diventa: new AndExpression(new GreaterThanExpression(new VariableExpression('cart.total'), new LiteralExpression(100)), new EqualsExpression(new VariableExpression('customer.tier'), new LiteralExpression('premium'))).
Chiamare $rule->interpret($context) restituisce true o false. Il contesto contiene ['cart.total' => 150, 'customer.tier' => 'premium']. La regola si valuta ricorsivamente senza un singolo if nel codice dell'interprete.
Esempio teorico: query language per filtri
Un'API REST offre filtri complessi: ?filter=status:active AND (category:electronics OR category:books) AND price:>50. Invece di parsare questa stringa con regex fragili, la trasformi in un albero di espressioni e la interpreti contro ogni record del database. L'Interpreter Pattern rende il parsing strutturato e la valutazione ricorsiva, pulita e testabile.
Interpreter e il Composite Pattern
L'Interpreter Pattern e strettamente legato al Composite Pattern: le espressioni formano un albero dove le nonterminal expression sono compositi e le terminal expression sono foglie. Il metodo interpret() attraversa l'albero ricorsivamente, esattamente come getTotal() attraversa l'albero di un preventivo nel Composite. La differenza e l'intento: il Composite modella strutture parte-intero, l'Interpreter modella grammatiche e le valuta.
Limiti e alternative
L'Interpreter Pattern funziona bene per linguaggi semplici con grammatiche piccole. Per linguaggi complessi (SQL, template engine, linguaggi di programmazione), l'albero di espressioni diventa ingestibile e servono tool specializzati: parser generator (ANTLR), PEG parser, o AST visitor combinati con l'Interpreter.
- Grammatiche con meno di 20 regole: Interpreter Pattern funziona bene
- Grammatiche con 20-50 regole: valuta se un parser generator e più manutenibile
- Grammatiche con 50+ regole: usa un parser generator o un framework dedicato
Quando usare l'Interpreter Pattern
- Usa l'Interpreter quando il dominio ha regole configurabili che cambiano frequentemente
- Usa l'Interpreter quando vuoi un mini-linguaggio per esprimere logica di business senza deploy
- Usa l'Interpreter quando la grammatica e semplice e le espressioni formano naturalmente un albero
- Non usare l'Interpreter per linguaggi complessi: il pattern non scala bene oltre una certa complessità
- Non usare l'Interpreter se le regole non cambiano mai: hardcodarle nel PHP e più semplice e performante
L'Interpreter Pattern e il pattern del linguaggio incorporato: dai al tuo dominio la capacità di esprimersi in una grammatica formale. E un pattern di nicchia ma potente: quando serve, niente altro lo sostituisce. Motori di regole, filtri configurabili, validatori dinamici — tutti beneficiano di un piccolo interprete ben progettato.
Top comments (0)