Low-level design2 min
Interpreter Pattern
Type: Behavioral Pattern
Relevance: Very Low. Rarely used in mainstream application development. Used mainly in compiler design or building DSLs (Domain Specific Languages).
The Interpreter pattern is used to define a grammatical representation for a language and provides an interpreter to deal with this grammar.
Real-Life Analogy
Think of a Musician reading Sheet Music. Sheet music is a specific language (grammar) representing notes, pitch, and tempo. The Musician acts as the Interpreter, reading the grammatical symbols (the sheet music) and converting them into physical sound.
The Problem
Imagine you are building a calculator app that needs to evaluate complex string math expressions like (5 + 3) * 2. Writing massive regex or string parsing loops gets incredibly messy. You need a structured way to parse the string into a syntax tree and evaluate it.
How to Implement Interpreter
You define an interface with an interpret() method. You create concrete classes for each grammar rule (e.g., NumberExpression, AddExpression). You then parse the input string into a tree of these Expression objects and call interpret() on the root.
Example in Code
// 1. The Abstract Expression
interface Expression {
interpret(): number;
}
// 2. Terminal Expression (The basic building blocks, e.g., Numbers)
class NumberExpression implements Expression {
constructor(private number: number) {}
interpret(): number {
return this.number;
}
}
// 3. Non-Terminal Expression (Operations that hold other expressions)
class AddExpression implements Expression {
constructor(private left: Expression, private right: Expression) {}
interpret(): number {
return this.left.interpret() + this.right.interpret();
}
}
class SubtractExpression implements Expression {
constructor(private left: Expression, private right: Expression) {}
interpret(): number {
return this.left.interpret() - this.right.interpret();
}
}
// Client Code
// Let's manually build the AST (Abstract Syntax Tree) for: (10 + 5) - 2
const ten = new NumberExpression(10);
const five = new NumberExpression(5);
const two = new NumberExpression(2);
// (10 + 5)
const addition = new AddExpression(ten, five);
// (addition) - 2
const subtraction = new SubtractExpression(addition, two);
// Evaluate the entire tree recursively!
console.log(`Result: ${subtraction.interpret()}`); // Output: 13
Class Diagram
Why is it Important?
- Extensibility: It's very easy to add new grammar rules (e.g., adding a
MultiplyExpression). - Niche Use Case: If you are building a SQL parser, a Regex engine, or a configuration language, this pattern is the foundation of how it works under the hood. However, for everyday business logic, it is almost never used.