Interpreter pattern is used to define a representation of a language’s grammar along with an interpreter that uses this representation to interpret sentences in that language. It is especially useful when a problem can be expressed in a simple grammar. The interpreter pattern involves defining an abstract expression for the grammar, concrete expressions for terminal and non-terminal symbols, and an interpreter that evaluates expressions based on the context.
Applicability
- When the grammar of a simple language needs to be interpreted or parsed
- When recurring expressions in a language can be represented using object-oriented classes
- Useful for domain-specific languages, mathematical expression parsing, or custom scripting
Pros and Cons
Advantages
- Easy to extend grammar
- You can add new rules or expressions by introducing new classes without altering the existing system.
- Readable object structure
- Grammars and expressions are represented in object-oriented form, making the logic more modular and easy to trace.
Drawbacks
- Complex for large grammars
- For complex or real-world grammars, the number of classes can grow rapidly, making the system harder to maintain.
- Low performance
- Because it uses a recursive structure and numerous objects, performance is often lower than dedicated parsers.
Approach
- Define the abstract expression: This is the base interface or abstract class for all expression types.
- Create terminal expressions: Represent the leaves of the grammar tree (e.g., literals, numbers).
- Create non-terminal expressions: These represent rules composed of other expressions (e.g.,
AddExpression
,SubtractExpression
). - Define context: A class that stores global information used during interpretation (e.g., a variable map).
- Build and interpret expressions: Construct an expression tree and call the
interpret()
method.
Components
- AbstractExpression
- Declares an
interpret(context)
method shared by all expressions.
- Declares an
- TerminalExpression
- Implements interpretation for a terminal symbol (literal, variable).
- NonTerminalExpression
- Represents rules involving other expressions, e.g., addition, subtraction.
- Context
- Holds variable mappings or external data needed during evaluation.
Example
The arithmetic expression interpreter showcases how expressions like (5 + 3) - 2
can be evaluated using interpreter pattern.
Abstract expression
interface Expression {
int interpret();
}
Terminal expression
class NumberExpression implements Expression {
private final int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
Non-terminal expression
class AddExpression implements Expression {
private final Expression left, right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
class SubtractExpression implements Expression {
private final Expression left, right;
public SubtractExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}
Client
public class InterpreterExample {
public static void main(String[] args) {
// Represent (5 + 3) - 2
Expression expr = new SubtractExpression(
new AddExpression(
new NumberExpression(5),
new NumberExpression(3)
),
new NumberExpression(2)
);
int result = expr.interpret();
System.out.println("Result: " + result); // Output: 6
}
}
Back to parent page: Behavioural Patterns
Design_Pattern Behavioural_Design_Patterns Interpreter_Pattern SOFT3202