DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Architecture and Code Design, Pt. 2: Polyglot Persistence Insights To Use Today and in the Upcoming Years
  • Double-Checked Locking Design Pattern in Java
  • Messaging Design Pattern (MDP) In Java

Trending

  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • Automatic Code Transformation With OpenRewrite
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • How to Convert XLS to XLSX in Java
  1. DZone
  2. Coding
  3. Java
  4. Using Interpreter Design Pattern In Java

Using Interpreter Design Pattern In Java

The Interpreter Design Pattern is one of the Gang of Four design patterns which specifies how to evaluate sentences in a language.

By 
Brijesh Saxena user avatar
Brijesh Saxena
DZone Core CORE ·
Oct. 29, 20 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
8.4K Views

Join the DZone community and get the full member experience.

Join For Free

Here I am with another design pattern to discuss — Interpreter Design Pattern. 

Interpreter Design Pattern

  • The Interpreter Design Pattern is one of the Gang of Four design patterns which specifies how to evaluate sentences in a language.
  • The Interpreter pattern is a behavioral pattern that provides a way to evaluate language grammar or expression.
  • The Interpreter pattern uses an expression interface that tells how to interpret a particular context.
  • The Interpreter pattern is used to define a grammatical representation for a language and also provides an interpreter to deal with the grammar.
  • The basic idea of the Interpreter pattern is to have a class for each symbol:
    • Terminal - are the elementary symbols of the language defined by a formal grammar. 
    • NonTerminal -  also known as syntactic variables that are replaced by groups of terminal symbols according to the production rules. The NonTerminal uses a composite design pattern in general.
  • A grammar is defined by production rules that specify which symbols may replace which other symbols. The production rules or simply called productions may be used to generate and parse strings.  
  • SQL interpreter is a good example of this pattern.
  • Language interpreters are another great example of this.
  • I have also used the Interpreter pattern in the example of the Command Design Pattern for interpreting the appliance and its operation to perform in the command string. 

interface expression

Employee Data Management Using Interpreter Design Pattern

Let's create a class for storing Employee data:

Java
 




x
86


 
1
package org.trishinfotech.interpreter;
2

          
3
public abstract class Employee {
4

          
5
    protected long employeeId;
6
    protected String employeeName;
7
    protected String designation;
8
    protected Department department;
9
    protected int salary;
10

          
11
    public Employee(long employeeId, String employeeName, String designation, Department department, int salary) {
12
        super();
13
        this.employeeId = employeeId;
14
        this.employeeName = employeeName;
15
        this.designation = designation;
16
        this.department = department;
17
        this.salary = salary;
18
    }
19

          
20
    public long getEmployeeId() {
21
        return employeeId;
22
    }
23

          
24
    public void setEmployeeId(long employeeId) {
25
        this.employeeId = employeeId;
26
    }
27

          
28
    public String getEmployeeName() {
29
        return employeeName;
30
    }
31

          
32
    public void setEmployeeName(String employeeName) {
33
        this.employeeName = employeeName;
34
    }
35

          
36
    public String getDesignation() {
37
        return designation;
38
    }
39

          
40
    public void setDesignation(String designation) {
41
        this.designation = designation;
42
    }
43

          
44
    public Department getDepartment() {
45
        return department;
46
    }
47

          
48
    public void setDepartment(Department department) {
49
        this.department = department;
50
    }
51

          
52
    public void setSalary(int salary) {
53
        this.salary = salary;
54
    }
55

          
56
    public int getSalary() {
57
        return salary;
58
    }
59

          
60
    public abstract int teamSize();
61

          
62
    public abstract String teamNames();
63

          
64
    public abstract boolean isManager();
65

          
66
    public String fullDetails() {
67
        StringBuilder builder = new StringBuilder();
68
        builder.append("Employee [").append(employeeId).append(", ").append(employeeName).append(", ")
69
                .append(designation).append(", ").append(department).append(", ").append(salary).append(", TeamSize=")
70
                .append(teamSize()).append(", Team=").append(teamNames()).append("]");
71
        return builder.toString();
72
    }
73

          
74
    public String shortDetails() {
75
        StringBuilder builder = new StringBuilder();
76
        builder.append("'").append(employeeName).append("'");
77
        return builder.toString();
78
    }
79

          
80
    @Override
81
    public String toString() {
82
        return fullDetails();
83
    }
84

          
85
}
86

          



Here's the code for the Engineer class:

Java
 




xxxxxxxxxx
1
25


 
1
package org.trishinfotech.interpreter;
2

          
3
public class Engineer extends Employee {
4

          
5
    public Engineer(long employeeId, String employeeName, String designation, Department department, int salary) {
6
        super(employeeId, employeeName, designation, department, salary);
7
    }
8

          
9
    @Override
10
    public int teamSize() {
11
        return 1;
12
    }
13

          
14
    @Override
15
    public boolean isManager() {
16
        return false;
17
    }
18

          
19
    @Override
20
    public String teamNames() {
21
        return "{NA}";
22
    }
23

          
24
}
25

          



Here's the code for the Manager class:

Java
 




xxxxxxxxxx
1
50


 
1
package org.trishinfotech.interpreter;
2

          
3
import java.util.ArrayList;
4
import java.util.List;
5
import java.util.stream.Collectors;
6

          
7
public class Manager extends Employee {
8

          
9
    List<Employee> managingEmployees = new ArrayList<Employee>();
10

          
11
    public Manager(long employeeId, String employeeName, String designation, Department department, int salary) {
12
        super(employeeId, employeeName, designation, department, salary);
13
    }
14

          
15
    public boolean manages(Employee employee) {
16
        return managingEmployees.add(employee);
17
    }
18

          
19
    public boolean stopManaging(Employee employee) {
20
        return managingEmployees.remove(employee);
21
    }
22

          
23
    public List<Employee> getManagingEmployees() {
24
        return managingEmployees;
25
    }
26

          
27
    @Override
28
    public int teamSize() {
29
        return managingEmployees.stream().mapToInt(employee -> employee.teamSize()).sum();
30
    }
31

          
32
    @Override
33
    public boolean isManager() {
34
        return true;
35
    }
36

          
37
    @Override
38
    public String teamNames() {
39
        StringBuilder builder = new StringBuilder();
40
        builder.append("{").append(String.join(", ", managingEmployees.stream().map(employee -> {
41
            if (employee.isManager()) {
42
                return employee.getEmployeeName() + " " + employee.teamNames();
43
            } else {
44
                return employee.getEmployeeName();
45
            }
46
        }).collect(Collectors.toList()))).append("}");
47
        return builder.toString();
48
    }
49
}
50

          



Here's the code for Department enum:

Java
 




xxxxxxxxxx
1
10


 
1
package org.trishinfotech.interpreter;
2

          
3
public enum Department {
4
    ENG, // engineering
5
    HR, // human resource
6
    ADMIN, // admin
7
    FACILITY, // facilities
8
    SUPPORT // customer support
9
}
10

          



Now, let's start implementing the Expression pattern. First, we define the interface for Expression:

Java
 




xxxxxxxxxx
1


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public interface Expression {
6
    
7
    public boolean interpret(Employee context);
8
}
9

          



Here's the code for TerminalExpression class:

Java
 




xxxxxxxxxx
1
10


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
public abstract class TerminalExpression implements Expression {
4

          
5
    public TerminalExpression() {
6
        super();
7
    }
8
    
9
}
10

          



Here's the code for NonTerminalExpression class:

Java
 




x


 
1
package org.trishinfotech.interpreter.expr;
2
3
public abstract class NonTerminalExpression implements Expression {
4
5
    protected Expression expression;
6
    
7
    public NonTerminalExpression() {
8
        super();
9
    }
10
11
    public Expression getExpression() {
12
        return expression;
13
    }
14
15
    public void setExpression(Expression expression) {
16
        this.expression = expression;
17
    }
18
    
19
}
20



I am using only TerminalExpression in the example and NonTerminalExpression is provided for reference and enhancement at your end.

Here's the code for NameExpression class:

Java
 




xxxxxxxxxx
1
26


 
 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public class NameExpression extends TerminalExpression {
6

          
7
    protected String name;
8

          
9
    public NameExpression(String name) {
10
        this.name = name;
11
    }
12

          
13
    @Override
14
    public boolean interpret(Employee context) {
15
        return context.getEmployeeName().equalsIgnoreCase(name);
16
    }
17

          
18
    @Override
19
    public String toString() {
20
        StringBuilder builder = new StringBuilder();
21
        builder.append("NameExpression [name=").append(name).append("]");
22
        return builder.toString();
23
    }
24

          
25
}
26

          



Here's the code for DesignationExpression class:

Java
 




xxxxxxxxxx
1
26


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public class DesignationExpression extends TerminalExpression {
6

          
7
    protected String designation;
8

          
9
    public DesignationExpression(String designation) {
10
        this.designation = designation;
11
    }
12

          
13
    @Override
14
    public boolean interpret(Employee context) {
15
        return context.getDesignation().equalsIgnoreCase(designation);
16
    }
17

          
18
    @Override
19
    public String toString() {
20
        StringBuilder builder = new StringBuilder();
21
        builder.append("DesignationExpression [designation=").append(designation).append("]");
22
        return builder.toString();
23
    }
24

          
25
}
26

          



Here's the code for DepartmentExpression class:

Java
 




xxxxxxxxxx
1
27


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public class DepartmentExpression extends TerminalExpression {
6

          
7
    protected String department;
8

          
9
    public DepartmentExpression(String department) {
10
        super();
11
        this.department = department;
12
    }
13

          
14
    @Override
15
    public boolean interpret(Employee context) {
16
        return context.getDepartment().name().equalsIgnoreCase(department);
17
    }
18

          
19
    @Override
20
    public String toString() {
21
        StringBuilder builder = new StringBuilder();
22
        builder.append("DepartmentExpression [department=").append(department).append("]");
23
        return builder.toString();
24
    }
25

          
26
}
27

          



Here's the code for ManagerOfExpression class:

Java
 




xxxxxxxxxx
1
35


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import java.util.stream.Collectors;
4

          
5
import org.trishinfotech.interpreter.Employee;
6
import org.trishinfotech.interpreter.Manager;
7

          
8
public class ManagerOfExpression extends TerminalExpression {
9

          
10
    protected String name;
11

          
12
    public ManagerOfExpression(String name) {
13
        this.name = name;
14
    }
15

          
16
    @Override
17
    public boolean interpret(Employee context) {
18
        if (context.isManager()) {
19
            Manager manager = (Manager) context;
20
            return manager.getManagingEmployees().stream()
21
                    .map(managingEmployee -> managingEmployee.getEmployeeName().toLowerCase())
22
                    .collect(Collectors.toList()).contains(name.toLowerCase());
23
        }
24
        return false;
25
    }
26

          
27
    @Override
28
    public String toString() {
29
        StringBuilder builder = new StringBuilder();
30
        builder.append("ManagerOfExpression [name=").append(name).append("]");
31
        return builder.toString();
32
    }
33

          
34
}
35

          



Here's the code for SalaryExpression class:

Java
 




xxxxxxxxxx
1
50


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public class SalaryExpression extends TerminalExpression {
6

          
7
    protected String salary;
8

          
9
    public SalaryExpression(String salary) {
10
        super();
11
        this.salary = salary;
12
    }
13

          
14
    @Override
15
    public boolean interpret(Employee context) {
16
        if (!Character.isDigit(salary.charAt(0))) {
17
            char operator = salary.charAt(0);
18
            return interpretIntSalary(context, salary.substring(1), operator);
19
        } else {
20
            return interpretIntSalary(context, salary, '=');
21
        }
22
    }
23

          
24
    private boolean interpretIntSalary(Employee context, String salary, char operator) {
25
        try {
26
            int intSalary = Integer.parseInt(salary);
27
            switch (operator) {
28
            case '>':
29
                return context.getSalary() > intSalary;
30
            case '<':
31
                return context.getSalary() < intSalary;
32
            case '+':
33
            case '-':
34
            default:
35
                return context.getSalary() == intSalary;
36
            }
37
        } catch (NumberFormatException exp) {
38
            return false;
39
        }
40
    }
41

          
42
    @Override
43
    public String toString() {
44
        StringBuilder builder = new StringBuilder();
45
        builder.append("SalaryExpression [salary=").append(salary).append("]");
46
        return builder.toString();
47
    }
48

          
49
}
50

          



Additionally, I am writing two more classes to combine expressions via 'And' or 'Or'.

Here's the code for AndExpression class:

Java
 




xxxxxxxxxx
1
29


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public class AndExpression implements Expression {
6

          
7
    protected Expression left;
8
    protected Expression right;
9

          
10
    public AndExpression(Expression left, Expression right) {
11
        super();
12
        this.left = left;
13
        this.right = right;
14
    }
15

          
16
    @Override
17
    public boolean interpret(Employee context) {
18
        return left.interpret(context) && right.interpret(context);
19
    }
20

          
21
    @Override
22
    public String toString() {
23
        StringBuilder builder = new StringBuilder();
24
        builder.append("AndExpression [left=").append(left).append(", right=").append(right).append("]");
25
        return builder.toString();
26
    }
27

          
28
}
29

          



Similarly, here's the code for OrExpression class:

Java
 




xxxxxxxxxx
1
29


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
import org.trishinfotech.interpreter.Employee;
4

          
5
public class OrExpression implements Expression {
6

          
7
    protected Expression left;
8
    protected Expression right;
9

          
10
    public OrExpression(Expression left, Expression right) {
11
        super();
12
        this.left = left;
13
        this.right = right;
14
    }
15

          
16
    @Override
17
    public boolean interpret(Employee context) {
18
        return left.interpret(context) || right.interpret(context);
19
    }
20

          
21
    @Override
22
    public String toString() {
23
        StringBuilder builder = new StringBuilder();
24
        builder.append("OrExpression [left=").append(left).append(", right=").append(right).append("]");
25
        return builder.toString();
26
    }
27

          
28
}
29

          



Again, I am using AndExpression only and I am providing OrExpression for your reference to use in your use case. 

Now let write a class to parse the expression. Here I am using the below format for the expression.

<Key>:<Value>, <Key>:<Value>

Having a pattern for parsing is recommended and that offers easy implementation, maintains an understanding of the expression. The format defines the grammar of the expression which is needed for the accurate evaluation of the expression.

Now since we are agreed on the format for expression, let's write a class for parsing the expression and create an Expression object. Writing parser is not part of the Interpreter Pattern and provided only for completing the code and example. 

Expression format and Parser are tightly coupled and we have to modify or use a different parser if we do change the expression format.

Here's the code for ExpressionParser class:

Java
 




x
43


 
1
package org.trishinfotech.interpreter.expr;
2

          
3
public class ExpressionParser {
4

          
5
    protected static final String NAME = "name";
6
    protected static final String DESIG = "desig";
7
    protected static final String DEPTT = "deptt";
8
    protected static final String MANAGES = "manages";
9
    protected static final String SALARY = "salary";
10

          
11
    public static Expression parseExpression(String contextString) {
12
        Expression expression = null;
13
        String[] keyValues = contextString.split(",");
14
        for (int index = 0; index < keyValues.length; index++) {
15
            String keyValue = keyValues[index];
16
            String[] words = keyValue.trim().split(":");
17
            Expression anotherExpression = getExpression(words[0].trim(), words[1].trim());
18
            if (expression == null) {
19
                expression = anotherExpression;
20
            } else {
21
                expression = new AndExpression(expression, anotherExpression);
22
            }
23
        }
24
        return expression;
25
    }
26

          
27
    public static Expression getExpression(String keyword, String value) {
28
        if (NAME.equalsIgnoreCase(keyword)) {
29
            return new NameExpression(value);
30
        } else if (DESIG.equalsIgnoreCase(keyword)) {
31
            return new DesignationExpression(value);
32
        } else if (DEPTT.equalsIgnoreCase(keyword)) {
33
            return new DepartmentExpression(value);
34
        } else if (MANAGES.equalsIgnoreCase(keyword)) {
35
            return new ManagerOfExpression(value);
36
        } else if (SALARY.equalsIgnoreCase(keyword)) {
37
            return new SalaryExpression(value);
38
        }
39
        return null;
40
    }
41

          
42
}
43

          



We can see that each expression concrete class is bound with a keyword to decode and evaluate.

Now since our, all component is ready to use for Expression pattern, let's write our Main class to execute and test the output:

Java
 




x


 
1
package org.trishinfotech.interpreter;
2

          
3
import org.trishinfotech.interpreter.expr.Expression;
4
import org.trishinfotech.interpreter.expr.ExpressionParser;
5

          
6
public class Main {
7

          
8
    public static void main(String[] args) {
9
        Engineer ajay = new Engineer(1001l, "Ajay", "Developer", Department.ENG, 75000);
10
        Engineer vijay = new Engineer(1002l, "Vijay", "Sr. Developer", Department.ENG, 90000);
11
        Engineer jay = new Engineer(1003l, "Jay", "Lead", Department.ENG, 100000);
12
        Engineer martin = new Engineer(1004l, "Martin", "QA", Department.ENG, 70000);
13
        Manager kim = new Manager(1005l, "Kim", "Manager", Department.ENG, 110000);
14
        Engineer andersen = new Engineer(1006l, "Andersen", "Developer", Department.ENG, 95000);
15
        Manager niels = new Manager(1007l, "Niels", "Sr. Manager", Department.ENG, 140000);
16
        Engineer robert = new Engineer(1008l, "Robert", "Developer", Department.ENG, 85000);
17
        Manager rachelle = new Manager(1009l, "Rachelle", "Product Manager", Department.ENG, 150000);
18
        Engineer shailesh = new Engineer(1010l, "Shailesh", "Engineer", Department.ENG, 80000);
19

          
20
        kim.manages(ajay);
21
        kim.manages(martin);
22
        kim.manages(vijay);
23

          
24
        niels.manages(jay);
25
        niels.manages(andersen);
26
        niels.manages(shailesh);
27

          
28
        rachelle.manages(kim);
29
        rachelle.manages(robert);
30
        rachelle.manages(niels);
31

          
32
        String contextString = "Desig:manager, Deptt:eng, Manages:martin, salary:110000";
33
        Expression expression = ExpressionParser.parseExpression(contextString);
34
        System.out.println("contextString= " + contextString);
35
        System.out.println();
36
        System.out.println(kim);
37
        System.out.printf("For '%s', %s: %s.\n", kim.getEmployeeName(), expression, expression.interpret(kim));
38

          
39
        System.out.println("=======================================================================\n");
40
        contextString = "Desig:developer, Deptt:eng, salary:<85000";
41
        expression = ExpressionParser.parseExpression(contextString);
42
        System.out.println("contextString= " + contextString);
43
        System.out.println();
44
        System.out.println(ajay);
45
        System.out.printf("For '%s', %s: %s.\n", ajay.getEmployeeName(), expression, expression.interpret(ajay));
46
        System.out.println();
47
        System.out.println(andersen);
48
        System.out.printf("For '%s', %s: %s.\n", andersen.getEmployeeName(), expression,
49
                expression.interpret(andersen));
50
    }
51

          
52
}
53

          



And below is the output of the program:

Java
 




xxxxxxxxxx
1
14


 
1
contextString= Desig:manager, Deptt:eng, Manages:martin, salary:110000
2

          
3
Employee [1005, Kim, Manager, ENG, 110000, TeamSize=3, Team={Ajay, Martin, Vijay}]
4
For 'Kim', AndExpression [left=AndExpression [left=AndExpression [left=DesignationExpression [designation=manager], right=DepartmentExpression [department=eng]], right=ManagerOfExpression [name=martin]], right=SalaryExpression [salary=110000]]: true.
5
=======================================================================
6

          
7
contextString= Desig:developer, Deptt:eng, salary:<85000
8

          
9
Employee [1001, Ajay, Developer, ENG, 75000, TeamSize=1, Team={NA}]
10
For 'Ajay', AndExpression [left=AndExpression [left=DesignationExpression [designation=developer], right=DepartmentExpression [department=eng]], right=SalaryExpression [salary=<85000]]: true.
11

          
12
Employee [1006, Andersen, Developer, ENG, 95000, TeamSize=1, Team={NA}]
13
For 'Andersen', AndExpression [left=AndExpression [left=DesignationExpression [designation=developer], right=DepartmentExpression [department=eng]], right=SalaryExpression [salary=<85000]]: false.
14

          



The Source Code can be found here: Interpreter-Design-Pattern-Sample-Code

I hope this tutorial helps in the understanding of the Interpreter design pattern.

Liked the article? Please don't forget to press that like button. Happy coding!

Java (programming language) Design Interpreter pattern

Opinions expressed by DZone contributors are their own.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Architecture and Code Design, Pt. 2: Polyglot Persistence Insights To Use Today and in the Upcoming Years
  • Double-Checked Locking Design Pattern in Java
  • Messaging Design Pattern (MDP) In Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!