Design Patterns Interpreter
The Interpreter pattern defines a small language and builds objects that evaluate sentences written in it. Each grammar rule becomes a class, and a sentence becomes a tree of those classes. Evaluating the tree produces the answer.
The Real-World Analogy
A musician reads sheet music. Each symbol carries a meaning: pitch, length, or pause. The musician translates the symbols into sound, one by one. Interpreter does the same job for text commands, where each symbol maps to an object that knows how to act.
Problem Without Interpreter
Users type rules like: "price > 100 AND category = books" Without a grammar model, the code fills with string splitting, nested if statements, and special cases that nobody can extend.
Solution With Interpreter
Expression: (5 + 3) - 2
┌───────────┐
│ Subtract │
└─────┬─────┘
┌───────┴───────┐
┌────┴────┐ ┌───┴───┐
│ Add │ │ Num 2 │
└────┬────┘ └───────┘
┌────┴────┐
┌───┴───┐ ┌───┴───┐
│ Num 5 │ │ Num 3 │
└───────┘ └───────┘
interpret() on the root walks down and returns 6.
Every node implements the same interpret method. Leaf nodes return values, and branch nodes combine results from their children.
Structure
┌─────────────────────────┐
│ «interface» Expression │
│ + interpret(): int │
└────────────▲────────────┘
┌───────┴─────────────┐
┌────┴──────────┐ ┌──────┴───────────────────┐
│ Terminal │ │ Nonterminal │
│ (numbers) │ │ (Add, Subtract) holds │
└───────────────┘ │ child expressions │
└──────────────────────────┘
Roles
- Expression: the common interface with an interpret method.
- Terminal Expression: a leaf that returns a direct value.
- Nonterminal Expression: a rule that combines other expressions.
- Client: builds the tree, often through a parser.
Code Example
interface Expression { int interpret(); }
class Number implements Expression {
private int value;
Number(int value) { this.value = value; }
public int interpret() { return value; }
}
class Add implements Expression {
private Expression left, right;
Add(Expression l, Expression r) { left = l; right = r; }
public int interpret() { return left.interpret() + right.interpret(); }
}
class Subtract implements Expression {
private Expression left, right;
Subtract(Expression l, Expression r) { left = l; right = r; }
public int interpret() { return left.interpret() - right.interpret(); }
}
// (5 + 3) - 2
Expression sentence =
new Subtract(
new Add(new Number(5), new Number(3)),
new Number(2));
print(sentence.interpret()); // 6Evaluation Flow
Subtract.interpret()
├─ Add.interpret()
│ ├─ Number(5) → 5
│ └─ Number(3) → 3 → 8
└─ Number(2) → 2
→ 8 - 2 = 6
Adding a New Rule
Multiplication arrives as one new class named Multiply. Existing classes stay unchanged, and sentences can use the new rule immediately.
Real Projects Using Interpreter
| Scenario | Language Example |
|---|---|
| Search filters | "color = red AND size = large" |
| Spreadsheet formulas | "=SUM(A1:A5) * 2" |
| Pattern matching | Simple text search rules |
| Business rules | Discount or eligibility conditions |
When to Avoid Interpreter
Large grammars create a swarm of classes. Performance suffers because tree walking costs more than compiled code. Parser generators and rule engines serve big languages better. Interpreter fits small, stable grammars.
Advantages and Disadvantages
| Advantages | Disadvantages |
|---|---|
| Easy to extend grammar | Many classes for big grammars |
| Each rule stays small and testable | Slower than compiled approaches |
| Clear mapping from grammar to code | Parsing needs separate work |
Step-by-Step Walkthrough
Trace the expression (5 + 3) - 2 in the code example. The client builds leaf objects for 5, 3, and 2. An Add object joins the first two. A Subtract object joins the result with 2. Calling interpret() on the Subtract starts a chain of calls that ends at the leaves.
The leaves return 5, 3, and 2. Add returns 8, and Subtract returns 6.
Where the Parser Fits
Users type text, such as "5 + 3 - 2", and a parser converts it into the expression tree. The Interpreter pattern covers evaluation. A hand-written or generated parser handles reading the text. Many projects keep the parser in a separate class that returns the root Expression.
Key Takeaways
- Each grammar rule becomes a class with an interpret method.
- Sentences form expression trees that evaluate from the leaves upward.
- The pattern suits small languages and simple rule sets.
- Large grammars deserve dedicated parser tools.
