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.
- The input expression (
- Expression Evaluation:
- The interpreter traverses the expression tree and starts interpreting each node.
- For terminal expressions (operands), such as
2
,3
, and4
, their respectiveinterpret()
methods return their numeric values. - For non-terminal expressions (operators), such as
+
and*
, theirinterpret()
methods recursively call theinterpret()
methods of their left and right sub-expressions and perform the respective operations (addition
andmultiplication
).
- 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 in12
. - Then, the
addition
operation (2 + 12
) is evaluated, resulting in the final interpretation value of14
.
- 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