Interpreter Design Pattern in Java
This article explains Interpreter design pattern in java with UML class diagram. It then takes an example scenario in java and explains it with class diagram and code.
Introduction Interpreter Design Pattern is a behavioral design pattern among the Gang Of Four(GOF) Design PatternsClick to read: Gang Of Four(GOF) Design Patterns. Being a behavioral design pattern, the Interpreter pattern deals with how objects work together - here, to evaluate sentences of a small language. What is Interpreter Design Pattern The Interpreter design pattern defines a representation for the grammar of a simple language, along with an interpreter which uses that representation to interpret sentences in the language. Each rule of the grammar becomes a class. A sentence - an expression such as
Class Diagram for Interpreter Design Pattern
Explanation of Interpreter Design Pattern's Class Diagram
The example interprets arithmetic expressions with variables. To keep the parser short, expressions are written in Reverse Polish Notation (RPN), where operators follow their operands:
Code for the classes shown in Java Example's Class Diagram
OUTPUT on running ExpressionParser.java
Explanation of Java Example's Class Diagram & Code
Summary
In the above tutorial we understood what is Interpreter design pattern and the main scenarios in which it is applicable. We then looked at the UML class diagram for Interpreter Design Pattern & its explanation, a Java use case which parses arithmetic expressions with variables into a syntax tree and interprets them, with its class diagram, code and output, followed by an explanation of both the class diagram & code. This concludes the tutorial on Interpreter design pattern.
Introduction Interpreter Design Pattern is a behavioral design pattern among the Gang Of Four(GOF) Design PatternsClick to read: Gang Of Four(GOF) Design Patterns. Being a behavioral design pattern, the Interpreter pattern deals with how objects work together - here, to evaluate sentences of a small language. What is Interpreter Design Pattern The Interpreter design pattern defines a representation for the grammar of a simple language, along with an interpreter which uses that representation to interpret sentences in the language. Each rule of the grammar becomes a class. A sentence - an expression such as
(x + 3) * y - is represented as a tree of objects of these classes, the abstract syntax tree, and interpreting the sentence means calling interpret() on the root of the tree, which calls it recursively on its children. The structure is a CompositeClick to read: Composite: simple expressions are leaves, and compound expressions contain other expressions.
Scenarios in which Interpreter Design Pattern can be used
- A simple language must be interpreted: When the application needs to evaluate expressions or rules written in a small language - formulas in a spreadsheet, filter expressions, business rules, simple query or configuration languages.
- The grammar is small and stable: When the grammar has few rules, so that one class per rule stays manageable. For large grammars, parser generators such as ANTLR are a better fit.
- Efficiency is not critical: When interpreting a tree of objects is fast enough. Performance-critical languages are usually compiled to another form instead.
AbstractExpressiondeclares theinterpret(Context)operation shared by every node of the syntax tree.TerminalExpressionimplementsinterpret()for the grammar's terminal symbols - the leaves of the tree, such as numbers and variable names.NonterminalExpressionrepresents a grammar rule made of other expressions, such as addition. It holds its operands and interprets itself by interpreting them and combining the results.Contextholds information global to the interpretation, such as the values of variables.Clientbuilds the syntax tree for a sentence - often with a parser - and callsinterpret()on its root.
(x + 3) * y is written x 3 + y *.
Expression.java - the abstract expression
//Expression.java
import java.util.Map;
public interface Expression {
int interpret(Map<String, Integer> context); // the context: variable values
}
Number.java and Variable.java - the terminal expressions
//Number.java
import java.util.Map;
class Number implements Expression {
private final int value;
Number(int value) { this.value = value; }
public int interpret(Map<String, Integer> context) { return value; }
public String toString() { return String.valueOf(value); }
}
//Variable.java
class Variable implements Expression {
private final String name;
Variable(String name) { this.name = name; }
public int interpret(Map<String, Integer> context) {
Integer value = context.get(name);
if (value == null) throw new IllegalArgumentException("no value for variable " + name);
return value;
}
public String toString() { return name; }
}
Add.java and Multiply.java - the nonterminal expressions
//Add.java
import java.util.Map;
class Add implements Expression {
private final Expression left, right;
Add(Expression left, Expression right) { this.left = left; this.right = right; }
public int interpret(Map<String, Integer> context) {
return left.interpret(context) + right.interpret(context);
}
public String toString() { return "(" + left + " + " + right + ")"; }
}
//Multiply.java
class Multiply implements Expression {
private final Expression left, right;
Multiply(Expression left, Expression right) { this.left = left; this.right = right; }
public int interpret(Map<String, Integer> context) {
return left.interpret(context) * right.interpret(context);
}
public String toString() { return left + " * " + right; }
}
ExpressionParser.java (with the main() method)
//ExpressionParser.java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
public class ExpressionParser {
// Builds the syntax tree from an expression in Reverse Polish Notation
static Expression parse(String rpn) {
Deque<Expression> stack = new ArrayDeque<>();
for (String token : rpn.trim().split("\\s+")) {
switch (token) {
case "+" -> { Expression right = stack.pop(); stack.push(new Add(stack.pop(), right)); }
case "*" -> { Expression right = stack.pop(); stack.push(new Multiply(stack.pop(), right)); }
default -> stack.push(token.matches("-?\\d+") ? new Number(Integer.parseInt(token)) : new Variable(token));
}
}
if (stack.size() != 1) throw new IllegalArgumentException("invalid expression: " + rpn);
return stack.pop();
}
public static void main(String[] args) {
Expression price = parse("quantity unitPrice * shipping +");
System.out.println("Syntax tree: " + price);
System.out.println("2 x 450 + 99 = " + price.interpret(Map.of("quantity", 2, "unitPrice", 450, "shipping", 99)));
System.out.println("5 x 120 + 0 = " + price.interpret(Map.of("quantity", 5, "unitPrice", 120, "shipping", 0)));
Expression formula = parse("x 3 + y *");
System.out.println("Syntax tree: " + formula + " with x=4, y=10 -> " + formula.interpret(Map.of("x", 4, "y", 10)));
try {
formula.interpret(Map.of("x", 4));
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Syntax tree: (quantity * unitPrice + shipping) 2 x 450 + 99 = 999 5 x 120 + 0 = 600 Syntax tree: (x + 3) * y with x=4, y=10 -> 70 Error: no value for variable y
Expressionis the abstract expression. The context is aMapfrom variable names to their values.NumberandVariableare terminal expressions: a number interprets to itself, and a variable to its value in the context, or an error if it has none.AddandMultiplyare nonterminal expressions. Each interprets its two operands - which may themselves be any expressions - and combines the results.ExpressionParseris the client. Itsparse()method turns an RPN string into a syntax tree using a stack: numbers and variables are pushed, and an operator pops its two operands and pushes the combined expression.- The tree is built once and can be interpreted many times with different contexts - here, the same pricing formula for two different orders. That separation of parsing from evaluation is what makes the pattern useful for rules and formulas which are defined once and evaluated often.
- Adding a new operation, such as subtraction, means adding one class and one parser case. If many different operations over the tree were needed - evaluation, pretty-printing, simplification - the Visitor patternClick to read: Visitor pattern, or Java 21's record patterns over a sealed interface, would keep them out of the expression classes.
java.util.regex.Pattern is an interpreter: Pattern.compile() parses a regular expression into a tree of node objects, which Matcher then interprets against input strings.
Gang of Four (GOF) Design Patterns on JavaBrahman
Creational Patterns: Abstract Factory PatternAbstract Factory Design Pattern in Java Builder PatternBuilder Design Pattern in Java Factory Method PatternFactory Method Design Pattern in Java Prototype PatternPrototype Design Pattern in Java Singleton PatternSingleton Design Pattern in Java
Structural Patterns: Adapter PatternAdapter Design Pattern in Java Bridge PatternBridge Design Pattern in Java Composite PatternComposite Design Pattern in Java Decorator PatternDecorator Design Pattern in Java Facade PatternFacade Design Pattern in Java Flyweight PatternFlyweight Design Pattern in Java Proxy PatternProxy Design Pattern in Java
Behavioral Patterns: Chain of Responsibility PatternChain of Responsibility Design Pattern in Java Command PatternCommand Design Pattern in Java Interpreter PatternInterpreter Design Pattern in Java Iterator PatternIterator Design Pattern in Java Mediator PatternMediator Design Pattern in Java Memento PatternMemento Design Pattern in Java Observer PatternObserver Design Pattern in Java State PatternState Design Pattern in Java Strategy PatternStrategy Design Pattern in Java Template Method PatternTemplate Method Design Pattern in Java Visitor PatternVisitor Design Pattern in Java
Analysis of Patterns: Strategy vs State PatternStrategy Design Pattern versus State Design Pattern GOF Patterns OverviewGOF / Gang of Four Design Patterns in Java
Creational Patterns: Abstract Factory PatternAbstract Factory Design Pattern in Java Builder PatternBuilder Design Pattern in Java Factory Method PatternFactory Method Design Pattern in Java Prototype PatternPrototype Design Pattern in Java Singleton PatternSingleton Design Pattern in Java
Structural Patterns: Adapter PatternAdapter Design Pattern in Java Bridge PatternBridge Design Pattern in Java Composite PatternComposite Design Pattern in Java Decorator PatternDecorator Design Pattern in Java Facade PatternFacade Design Pattern in Java Flyweight PatternFlyweight Design Pattern in Java Proxy PatternProxy Design Pattern in Java
Behavioral Patterns: Chain of Responsibility PatternChain of Responsibility Design Pattern in Java Command PatternCommand Design Pattern in Java Interpreter PatternInterpreter Design Pattern in Java Iterator PatternIterator Design Pattern in Java Mediator PatternMediator Design Pattern in Java Memento PatternMemento Design Pattern in Java Observer PatternObserver Design Pattern in Java State PatternState Design Pattern in Java Strategy PatternStrategy Design Pattern in Java Template Method PatternTemplate Method Design Pattern in Java Visitor PatternVisitor Design Pattern in Java
Analysis of Patterns: Strategy vs State PatternStrategy Design Pattern versus State Design Pattern GOF Patterns OverviewGOF / Gang of Four Design Patterns in Java