Interpreter Design Pattern in Java

1. Definition

The Interpreter Design Pattern provides a way to evaluate language grammar or expressions for particular languages. It involves implementing an expression interface that tells how to interpret a particular context.

2. Problem Statement

How can you design a system that can interpret sentences or expressions for a particular language or grammar, especially when there are many sentences or expressions to be interpreted?

3. Solution

Define a grammar for the language, and then build an interpreter to interpret the sentences of that language. For each rule in the grammar, define an interpreter class.

4. Real-World Use Cases

1. SQL parsers interpret SQL queries.

2. Regular expression engines interpret regular expressions.

3. Code compilers interpret the source code of particular languages.

5. Implementation Steps

1. Define an abstract Expression class or an interface.

2. Implement one concrete subclass for each grammar rule.

3. Use the interpreter by first building an abstract syntax tree of the expression and then calling interpret() on the tree.

6. Implementation

// Expression interface
interface Expression {
    boolean interpret(String context);
}

// Terminal Expression
class TerminalExpression implements Expression {
    private String data;

    public TerminalExpression(String data) {
        this.data = data;
    }

    @Override
    public boolean interpret(String context) {
        if (context.contains(data)) {
            return true;
        }
        return false;
    }
}

// OrExpression
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);
    }
}

// AndExpression
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);
    }
}

// Client
public class InterpreterPatternDemo {
    public static void main(String[] args) {
        Expression isMale = new OrExpression(new TerminalExpression("John"), new TerminalExpression("Robert"));
        Expression isMarried = new AndExpression(new TerminalExpression("Julie"), new TerminalExpression("Married"));

        System.out.println("Is John male? " + isMale.interpret("John"));
        System.out.println("Is Julie a married woman? " + isMarried.interpret("Married Julie"));
    }
}

Output:

Is John male? true
Is Julie a married woman? true

Explanation

The Interpreter pattern provides a way to evaluate sentences in a language. In this example, the expressions check whether a given string (like "John" or "Married Julie") matches certain conditions. Different concrete expressions (like AndExpression, OrExpression, and TerminalExpression) represent different types of evaluations.

7. When to use?

Use the Interpreter pattern when:

1. The grammar of the language is simple. For complex grammars, tools like parser generators are more suitable.

2. Efficiency is not critical. The pattern can be slow and complex.

3. You have a particular language or grammar that you want to represent and interpret.


Comments