Design Patterns Simplified: Part 22 — Interpreter Pattern (a.k.a. “The Rule Engine”)

The Interpreter Pattern belongs to the Behavioral category of design patterns.
Why? Because it is all about defining a simple grammar and then interpreting statements written in that grammar.

Bigger question is why do we even need it?
The Interpreter Pattern is used when your system needs to read, understand, and act on sentences written in a mini language (domain-specific language). Instead of hardcoding all possible variations, you define a grammar (rules) and let the system evaluate them dynamically.

Think about traffic signs. A red circle with a line means No Entry, a green light means Go. You do not memorize every road individually, instead, just interpret symbols using predefined rules.

It applies effectively to below scenarios too.

  • Search filters → “category = ‘Electronics’ AND price < 1000”
  • Validation rules → “Age > 18 AND Country = ‘India'”
  • Math expressions → “5 + 10 – 2”

Each of these is a sentence in some domain specific language.
The Interpreter Pattern lets you define the grammar (rules + symbols) and then evaluate/interpret sentences accordingly.

The Language Teacher

Suppose you are learning Spanish. Instead of memorizing every possible sentence, your teacher gives you rules,

“hola” → “hello”
“gracias” → “thank you”
Grammar: Sentence = Word1 + Word2 + …

Now, when you see “hola amigo”, you don’t need a separate hardcoded translation. You apply rules recursively to interpret the whole sentence.

That is the Interpreter Pattern in action – Rules + Recursive interpretation.

Search Filters in E-commerce

Let’s say you are building a product search system for an e-commerce application. Users can define filters like,

Category = “Electronics”
AND
Price < 1000

You want your system to interpret this rule and filter products accordingly.

What Does the Code Look Like?

// Step 1: Common Expression interface
Interface Expression
    Method Interpret(product) : bool

// Step 2: Terminal Expressions (basic rules)
Class CategoryExpression implements Expression
    Property category

    Constructor(category)
        this.category = category

    Method Interpret(product)
        return product.Category == category

Class PriceExpression implements Expression
    Property priceLimit

    Constructor(priceLimit)
        this.priceLimit = priceLimit

    Method Interpret(product)
        return product.Price < priceLimit

// Step 3: Non-Terminal Expressions (combinators)
Class AndExpression implements Expression
    Property left : Expression
    Property right : Expression

    Constructor(left, right)
        this.left = left
        this.right = right

    Method Interpret(product)
        return left.Interpret(product) AND right.Interpret(product)

Class OrExpression implements Expression
    Property left : Expression
    Property right : Expression

    Constructor(left, right)
        this.left = left
        this.right = right

    Method Interpret(product)
        return left.Interpret(product) OR right.Interpret(product)

// caller logic
products = [
    {Name:"iPhone", Category:"Electronics", Price:900},
    {Name:"Shoes", Category:"Fashion", Price:120},
    {Name:"Laptop", Category:"Electronics", Price:1500}
]

// Rule: Category = "Electronics" AND Price < 1000
rule = new AndExpression(
            new CategoryExpression("Electronics"),
            new PriceExpression(1000))

// Evaluate rule against all products
For each product in products
    If rule.Interpret(product)
        Print "Selected: " + product.Name

// output:
Selected: iPhone

Here, we defined a common Expression interface.
CategoryExpression and PriceExpression are terminal expressions → basic rules.

AndExpression and OrExpression are non-terminal expressions → combine rules.

The client builds a rule tree (Electronics AND Price < 1000), and the system evaluates each product using Interpret().

What Did We Achieve?

  • Abstraction of rules : Client does not hardcode logic for category/price.
  • Extensibility : You can add new filters (BrandExpression, RatingExpression) easily.
  • Reusability : Complex rules can be built from smaller ones.
  • Flexibility : Rules are simply objects, which can be stored, combined, reused.

When Should You Use the Interpreter Pattern?

  • When you have to process and evaluate expressions (math, rules, filters).
  • When the grammar is simple and stable.
  • When you want to allow dynamic rules instead of hardcoding logic.

Use Cases?

  • E-commerce search filters
  • Rule-based engines (validation, business policies)
  • Query interpreters (SQL-like structures in domain specific language)
  • Formula calculation engines

Note:

  • Interpreter pattern is not about machine translation like English → French. It is about defining a grammar and providing a way to interpret statements written in that grammar.
  • If the grammar is very complex, Interpreter can lead to large class hierarchies. In such cases, parser generators or compilers are a better fit.
  • People confuse it with these patterns,
    Strategy Pattern (choosing one algorithm at runtime) – but Interpreter is about grammar + evaluating sentences, not choosing algorithms.
    Facade Pattern (simplifying subsystems) – Interpreter is not about simplification, it is about parsing + evaluating rules.

To summarize, it would be apt to say,

The Interpreter Pattern is like having a language teacher for your code. Instead of hardcoding every possible sentence, you define rules (grammar), and the system interprets them recursively.

It shines when you want flexible, extensible, rule-based evaluation.

Hope now you have a good understanding of the Interpreter pattern and its applications.

This marks the end of our series – Design Patterns Simplified.

Next up: Design Patterns Simplified – Cheat Sheet. The only design pattern sheet you need to look at before your next tech interview.

Similar Posts