Behavioral · Gang of Four
Interpreter
Interpreter is a behavioral pattern that turns a small language into an object tree you can evaluate. Each expression node knows how to interpret itself against a context.
Example: a product manager writes status = PAID AND amount > 500. You can bury that in nested ifs inside search(). Next week they add OR and another field. An expression tree grows with the grammar instead of with one giant method.
The sentence as a tree
And sits on top; leaves do the facts
Walk starts at And. And asks both children. Each looks at one field. A never knows Ticket’s internals.
combinator
And
left.matches && right.matches
terminal
StatusIs PAID
terminal
GreaterThan 500
Is this a tiny language, or a pile of ifs?
| Smell | Honest shape | Not this pattern |
|---|---|---|
| User types a filter string | Interpreter: parse to an | One giant evaluate() that also tokenizes |
| Rules are code you compose | Specification | A parser you do not need |
| Folder / file tree, same contract | Composite | Calling the tree a language |
| Named predicates + AND/OR in a product | Rule engine (often Interpreter + registry) | A full programming language |
Java: a filter sentence as objects
Two terminals and And/Or show the idea. Stop there unless they ask you to extend the grammar. Keep parse (string → tree) in a different type from matches (tree → boolean).
interface Filter {
boolean matches(Ticket t);
}
class StatusIs implements Filter {
private final Status status;
StatusIs(Status status) { this.status = status; }
public boolean matches(Ticket t) { return t.status() == status; }
}
class GreaterThan implements Filter {
private final Money min;
GreaterThan(Money min) { this.min = min; }
public boolean matches(Ticket t) { return t.amount().greaterThan(min); }
}
class And implements Filter {
private final Filter left, right;
And(Filter left, Filter right) { this.left = left; this.right = right; }
public boolean matches(Ticket t) { return left.matches(t) && right.matches(t); }
}
class Or implements Filter {
private final Filter left, right;
Or(Filter left, Filter right) { this.left = left; this.right = right; }
public boolean matches(Ticket t) { return left.matches(t) || right.matches(t); }
}
Filter paidBig = new And(new StatusIs(Status.PAID), new GreaterThan(Money.of(500)));Keep the language tiny
If you need precedence, a real parser, and types, you are past machine-coding. Show a hand-built tree for AND/OR and two predicates. That proves you can turn a sentence into objects instead of a 400-line evaluate().
Visitor is a different pass
Interpreter evaluates a grammar. Visitor is a new walk over a tree you already have — tax, pretty-print, export — without editing File and Folder. See Visitor.
Split build from evaluate
When the language is too big
Specification versus Interpreter
One giant parse method that also evaluates
Split “build the tree” from “ask the tree.” Mixing them is how search() becomes untestable.
A full programming language
Two terminals and And/Or show the idea. Stop there unless they ask you to extend the grammar.
Worked filter: build, then ask
Tokenize status = PAID AND amount > 500 into StatusIs and GreaterThan, wrap with And, then call matches on a Ticket. Tests construct the tree by hand — they do not have to go through the parser. Keeping those two jobs separate is the design.
Parser
String in, Filter out. Recursive descent for AND/OR is enough. Do not put Ticket rules here.
Tree
matches(Ticket) only. Unit tests new And(...) without a string. That is how you prove the objects are the language.
Java: Not is still a combinator
Once And and Or exist, Not is one class. A rule engine registry maps the word PAID to StatusIs. Keep that registry out of Filter.
class Not implements Filter {
private final Filter inner;
Not(Filter inner) { this.inner = inner; }
public boolean matches(Ticket t) { return !inner.matches(t); }
}
Filter unpaid = new Not(new StatusIs(Status.PAID));
Filter search = new And(unpaid, new GreaterThan(Money.of(500)));Search box
User-typed query → tree. That is Interpreter. Hard-coded predicates in Java are Specification.
Precedence
If they ask AND vs OR, parenthesize or use a tiny Pratt/recursive descent. Do not hand-wave a 20-token grammar.
Stop the language
No loops, no variables, no user functions. Offer one extra terminal (date range) if they want depth.
Nearby in LLD