Design Pattern (28) - Interpreter Pattern

You can download the Design Pattern series code from this design_pattern repo.


Requirements

In modern software design and system architecture, we often need to handle complex logical operations and expression parsing. Using the Interpreter Pattern can create a highly extensible and maintainable boolean operation interpreter system. This behavioral design pattern is very useful in programming for handling rule engines and domain-specific languages.

This system using object-oriented programming (OOP) principles needs to have the following core functionalities:

  1. Expression Interpretation Capability: Correctly interpret complex expressions containing boolean values, AND operations, and OR operations.
  2. Extensibility: Comply with Open-Closed Principle, conveniently add other operators (such as NOT, XOR, etc.).
  3. Maintainability: Clear system structure, code easy to understand, maintain, and extend.

Through this example, we will learn how to use the Interpreter Pattern to elegantly solve expression parsing problems.


Object-Oriented Analysis (OOA)

Before starting design, we need to deeply understand the problem’s essence. Let’s clarify the system’s core structure through object-oriented analysis.

Recognizing Forces

During requirement analysis, we discovered several key design challenges:

  1. Problem of Increasing Complexity
    • When the system needs to support more operator types, traditional manual parsing logic becomes extremely complex.
    • Code readability and maintainability decline sharply as the number of operators increases.
  2. Duplicate Code Troubles
    • Processing logic for different operators often has similar patterns, leading to extensive duplicate code.
    • This duplication not only increases maintenance costs but also raises error risks.
  3. Extensibility Challenges
    • Every time we need to add operators, developers must modify code in multiple places.
    • This approach violates the Open-Closed Principle (OCP), making the system fragile and difficult to maintain.

These problems prompted us to seek a more elegant solution, and the Interpreter Pattern is the ideal choice for addressing these challenges.


Applying Interpreter Pattern (Solution) to Get New Context (Resulting Context)

After completing object-oriented analysis and identifying design challenges, we can now use the Interpreter Pattern to solve these problems. The Interpreter Pattern provides an elegant architecture that allows us to systematically handle expression parsing.

Core Architecture of Interpreter Pattern

Let’s first understand the standard UML structure of Interpreter Pattern:

Core Components of Interpreter Pattern

The Interpreter Pattern mainly consists of the following three key components:

  1. Abstract Expression (Expression)
    • Defines common interface for all expressions, establishing unified processing specifications.
    • Ensures different types of expressions can be called and processed in the same way.
  2. Terminal Expression
    • Handles basic units in syntax, such as boolean values true and false.
    • These are leaf nodes in expression trees, containing no other sub-expressions.
  3. Non-Terminal Expression
    • Represents complex operation combinations, such as AND and OR operators.
    • Handles sub-expressions through recursive methods, implementing interpretation of compound logic.

Core Idea of the Pattern

The essence of the Interpreter Pattern lies in treating each operator and operand as an independent “expression” object. Through recursive composition, we can break down complex boolean operations into multiple small and reusable units.

This design approach not only maintains system structure clarity but more importantly provides excellent flexibility and extensibility.

Application to Our Boolean Operation System

Now let’s specifically apply the Interpreter Pattern to our boolean operation interpreter system:


Object-Oriented Programming (OOP)

With a clear design architecture, let’s transform the Interpreter Pattern into concrete Kotlin code. We’ll implement each component step by step and show how they work together.

Abstract Expression: Expression

First define the common interface for all expressions. This interface ensures all expression classes have the same interpretation capability:

interface Expression {
    fun interpret(): Boolean
}

Terminal Expression: BooleanValue

Terminal expressions represent basic values in boolean operations. They are leaf nodes in expression trees, directly returning stored boolean values:

class BooleanValue(private val value: Boolean) : Expression {
    override fun interpret(): Boolean = value
}

This class design is very concise, but it’s the foundational building block of the entire system.

Non-Terminal Expressions: AndExpression, OrExpression

Non-terminal expressions handle complex logical operations. They contain other sub-expressions and interpret through recursive methods:

class AndExpression(private val left: Expression, private val right: Expression) : Expression {
    override fun interpret(): Boolean = left.interpret() && right.interpret()
}

class OrExpression(private val left: Expression, private val right: Expression) : Expression {
    override fun interpret(): Boolean = left.interpret() || right.interpret()
}

These two classes demonstrate the core characteristic of the Interpreter Pattern: implementing complex logic through composition of sub-expressions.

Client Code Implementation

Now let’s see how to use these classes to construct and interpret boolean expressions:

fun main() {
    // Define boolean expression: true AND false OR true
    val expression = OrExpression(
        AndExpression(
            BooleanValue(true),
            BooleanValue(false)
        ),
        BooleanValue(true)
    )

    // Calculate result
    val result = expression.interpret()
    println("Result of the expression is: $result")
}

This example demonstrates the construction process of expression (true AND false) OR true. By combining different expression objects, we created an expression tree and can easily evaluate it through the interpret() method.

Execution Results

Result of the expression is: true

This result verifies our implementation correctness: (true AND false) is false, but false OR true final result is true.


Conclusion

Through this boolean operation system implementation, we deeply explored how the Interpreter Pattern elegantly solves expression parsing challenges. This design pattern brings us significant advantages.

Core Advantages of Interpreter Pattern

  1. Clear and Intuitive Structure
    • Each operator and operand is encapsulated into independent expression classes.
    • This organization makes system architecture clear at a glance, easy to understand and maintain.
  2. Excellent Extensibility
    • When needing to add operators (like NOT, XOR), only need to implement new expression classes.
    • This fully complies with Open-Closed Principle (OCP), open for extension and closed for modification.
  3. High Flexibility
    • Supports dynamic construction of complex expression structures.
    • Can easily combine different operators, adapting to various business scenario requirements.

Practical Application Scenarios

Interpreter Pattern is particularly suitable for the following scenarios:

  • Configuration file rule parsing
  • Mathematical expression calculators
  • SQL query condition parsing
  • Business rule engines

Usage Considerations

Although Interpreter Pattern has many advantages, we need to pay attention to its applicable scope when using it. This pattern is more suitable for handling relatively simple syntax rules.

If facing overly complex syntax structures, it might lead to significant increase in class numbers, actually affecting system maintainability. In such cases, we can consider combining with other design patterns (like Composite Pattern or Visitor Pattern) for optimization.

Overall, Interpreter Pattern provides a structured and extensible solution for expression parsing problems, making it an important tool in software architects’ toolkits.




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Claude Code 使用技巧與最佳實踐 - Tips and Best Practices
  • 🤖 AI Agent Series (Part 1): Understanding the Core Interaction Logic of LLM, RAG, and MCP
  • 💡 Managing Multiple GitHub Accounts on One Computer: The Simplest SSH Configuration Method
  • 🚀 How to Use Excalidraw AI to Quickly Generate Professional Diagrams and Boost Work Efficiency!
  • Complete macOS Development Environment Setup Guide: Mobile Development Toolchain Configuration Tutorial