Interpreter Design Pattern example

Suppose we have a simple language that supports basic arithmetic operations, such as addition (+), subtraction (-), multiplication (*), and division (/). We want to create a calculator program that can interpret and evaluate arithmetic expressions written in this language.

Benefits of using the Interpreter Pattern:

The Interpreter pattern can be applied to this scenario to provide a structured way of interpreting and evaluating arithmetic expressions. Its benefits include:

  • Modularity: Components such as terminal and non-terminal expressions can be easily added or modified to support new language constructs or operations.
  • Separation of Concerns: The pattern separates the grammar interpretation from the client, allowing the client to focus on providing input expressions while leaving the interpretation logic to the interpreter components.
  • Extensibility: New operations or language constructs can be added without modifying existing code, promoting code reuse and maintainability.

Communication flow of the Interpreter Design pattern using expression ” 2+3*4 ” :

  • Client: The client initiates the interpretation process by creating an interpreter object and providing the input expression (2 + 3 * 4).
  • Interpreter Initialization: The interpreter object is created, along with any necessary context object (if applicable). In our case, we’ll assume a simple context object is created.
  • Parsing and Expression Tree Building:
    • The input expression (2 + 3 * 4) is parsed to create an expression tree representing the structure of the expression.
    • Each operator and operand in the expression is represented by a corresponding expression object.
  • Expression Evaluation:
    • The interpreter traverses the expression tree and starts interpreting each node.
    • For terminal expressions (operands), such as 2, 3, and 4, their respective interpret() methods return their numeric values.
    • For non-terminal expressions (operators), such as + and *, their interpret() methods recursively call the interpret() methods of their left and right sub-expressions and perform the respective operations (addition and multiplication).
  • Combining Interpretations:
    • The interpreter combines the interpretations of sub-expressions according to the rules defined by the expression tree.
    • In our example, the multiplication operation (3 * 4) is evaluated first, resulting in 12.
    • Then, the addition operation (2 + 12) is evaluated, resulting in the final interpretation value of 14.
  • Output: The final interpretation result (14) is returned to the client, which can then use it for further processing or display.

Below is the code of above problem statement using Interpreter Pattern:

Let’s break down into the component wise code:

1. Client

The client provides input expressions and interacts with the interpreter.

Java




public class Client {
    public static void main(String[] args) {
        // Input expression
        String expression = "2 + 3 * 4";
         
        // Create interpreter
        Context context = new Context();
        Interpreter interpreter = new Interpreter(context);
         
        // Interpret expression
        int result = interpreter.interpret(expression);
        System.out.println("Result: " + result);
    }
}


2. Context

The context holds global information needed for interpretation.

Java




public class Context {
    // Any global information needed for interpretation
}


3. Abstract Expression

Defines the common interface for interpreting expressions.

Java




public interface Expression {
    int interpret(Context context);
}


4. Terminal Expression

Represents basic language elements.

Java




public class NumberExpression implements Expression {
    private int number;
 
    public NumberExpression(int number) {
        this.number = number;
    }
 
    @Override
    public int interpret(Context context) {
        return number;
    }
}


5. Non-Terminal Expression

Represents composite language constructs.

Java




public class AdditionExpression implements Expression {
    private Expression left;
    private Expression right;
 
    public AdditionExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
 
    @Override
    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }
}
 
public class MultiplicationExpression implements Expression {
    private Expression left;
    private Expression right;
 
    public MultiplicationExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
 
    @Override
    public int interpret(Context context) {
        return left.interpret(context) * right.interpret(context);
    }
}


Complete code for the above example

Below is the complete code for the above example:

Java




class Context {
    // Any global information needed for interpretation
}
 
interface Expression {
    int interpret(Context context);
}
 
class NumberExpression implements Expression {
    private int number;
 
    public NumberExpression(int number) {
        this.number = number;
    }
 
    @Override
    public int interpret(Context context) {
        return number;
    }
}
 
class AdditionExpression implements Expression {
    private Expression left;
    private Expression right;
 
    public AdditionExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
 
    @Override
    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }
}
 
class MultiplicationExpression implements Expression {
    private Expression left;
    private Expression right;
 
    public MultiplicationExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
 
    @Override
    public int interpret(Context context) {
        return left.interpret(context) * right.interpret(context);
    }
}
 
class Interpreter {
    private Context context;
 
    public Interpreter(Context context) {
        this.context = context;
    }
 
    public int interpret(String expression) {
        // Parse expression and create expression tree
        Expression expressionTree = buildExpressionTree(expression);
         
        // Interpret expression tree
        return expressionTree.interpret(context);
    }
 
    private Expression buildExpressionTree(String expression) {
        // Logic to parse expression and create expression tree
        // For simplicity, assume the expression is already parsed
        // and represented as an expression tree
        return new AdditionExpression(
            new NumberExpression(2),
            new MultiplicationExpression(
                new NumberExpression(3),
                new NumberExpression(4)
            )
        );
    }
}
 
public class Client {
    public static void main(String[] args) {
        // Input expression
        String expression = "2 + 3 * 4";
         
        // Create interpreter
        Context context = new Context();
        Interpreter interpreter = new Interpreter(context);
         
        // Interpret expression
        int result = interpreter.interpret(expression);
        System.out.println("Result: " + result);
    }
}


Output




Result: 14


Interpreter Design Pattern

The Interpreter design pattern is a behavioral design pattern that facilitates the interpretation and evaluation of expressions or language grammars.

Important Topics for the Interpreter Design Pattern

  • What is the Interpreter Design Pattern?
  • Components of the Interpreter Design Pattern
  • Real-Life analogy of Interpreter Design Pattern
  • Interpreter Design Pattern example
  • When to use the Interpreter Design Pattern
  • When not to use the Interpreter Design Pattern

Similar Reads

What is the Interpreter Design Pattern?

The Interpreter design pattern is a behavioral design pattern that defines a way to interpret and evaluate language grammar or expressions. It provides a mechanism to evaluate sentences in a language by representing their grammar as a set of classes. Each class represents a rule or expression in the grammar, and the pattern allows these classes to be composed hierarchically to interpret complex expressions....

Components of the Interpreter Design Pattern

1. AbstractExpression...

Real-Life analogy of Interpreter Design Pattern

Imagine you are traveling to a foreign country where you do not speak the native language. In such a scenario, you may need the assistance of an interpreter to help you communicate effectively with the locals....

Interpreter Design Pattern example

Suppose we have a simple language that supports basic arithmetic operations, such as addition (+), subtraction (-), multiplication (*), and division (/). We want to create a calculator program that can interpret and evaluate arithmetic expressions written in this language....

When to use Interpreter Design Pattern

...

When not to use Interpreter Design Pattern

...