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());    // 6

Evaluation 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

ScenarioLanguage Example
Search filters"color = red AND size = large"
Spreadsheet formulas"=SUM(A1:A5) * 2"
Pattern matchingSimple text search rules
Business rulesDiscount 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

AdvantagesDisadvantages
Easy to extend grammarMany classes for big grammars
Each rule stays small and testableSlower than compiled approaches
Clear mapping from grammar to codeParsing 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.