Interpreter Design Pattern in Java
1. Introduction
The Interpreter Pattern defines a way to evaluate sentences (or expressions) in a language. It is useful when you have:
- A simple grammar.
- Repeated evaluation of expressions defined in that grammar.
2. Real-Life Analogy
Think about a calculator:
- If you type 2 + 3, the calculator interprets it as an expression and evaluates the result.
- Similarly, in software, if you define rules like "check if a number is even AND greater than 10", you can use the interpreter pattern to evaluate that.
3. Structure
- Expression (Interface) → Declares an interpret() method.
- TerminalExpression → Represents atomic values (like numbers or variables).
- NonTerminalExpression → Represents rules/operations (AND, OR, etc.).
- Context → Holds global information used during interpretation.
4. Custom Java Implementation Example
We’ll create a simple rule engine that interprets whether a given string satisfies some conditions using AND and OR expressions.
Step 1: Expression Interface
interface Expression {
boolean interpret(String context);
}
Step 2: Terminal Expression (Basic Rule)
class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
This checks if the input contains a specific word.
Step 3: Non-Terminal Expressions (Rules)
class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
class AndExpression implements Expression {
private Expression expr1;
private Expression expr2;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
Step 4: Client (Using the Interpreter)
public class InterpreterPatternDemo {
// Rule: "John" OR "Mike"
public static Expression getMaleExpression() {
Expression john = new TerminalExpression("John");
Expression mike = new TerminalExpression("Mike");
return new OrExpression(john, mike);
}
// Rule: "Julie" AND "Married"
public static Expression getMarriedWomanExpression() {
Expression julie = new TerminalExpression("Julie");
Expression married = new TerminalExpression("Married");
return new AndExpression(julie, married);
}
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarriedWomanExpression();
System.out.println("John is male? " + isMale.interpret("John"));
System.out.println("Julie is a married woman? " + isMarriedWoman.interpret("Married Julie"));
System.out.println("Mike is male? " + isMale.interpret("Mike"));
System.out.println("Alice is male? " + isMale.interpret("Alice"));
}
}
5. Output
John is male? true
Julie is a married woman? true
Mike is male? true
Alice is male? false
6. Benefits
- Easy to extend the grammar (just add new expressions).
- Makes it possible to evaluate custom rules dynamically.
- Decouples grammar definition from evaluation logic.
7. When to Use
- When the grammar is simple and stable.
- For parsing and evaluating expressions like SQL, regex, or business rules.
- In scenarios like compilers, calculators, and interpreters for domain-specific languages.