This behavioral pattern is used to represent the language, define its rules along with an interpreter to interpret these rules. Use the interpreter when a set of problems occurs frequently.
We map each sentence in the grammar to a specific type, and each type contributes to the process of interpreting the sentences. Now let’s take a look at the design of the interpreter pattern.
The expression protocol defines a single method requirement, interpret,which takes a context argument. The context represents a global state that is used as input and output by each expression type. We can have two kinds of expressions. The Terminal Expression represents an expression that does not contain other expressions, whereas the Non-terminal Expression maps a compound symbol in the grammar. A compound expression either relies on multiple terminal expressions, or it calls itself recursively until it reaches a Terminal Expression. The client parses the sentences, builds the list of Terminal and Non-Terminal Expressions, and invokes their interpret method sequentially.
An expressions that contain other expressions are called non-terminal expressions.
The interpreter design pattern defines a language with a simple grammar. It provides an object representation of each grammar rule, along with an interpreter to interpret the grammar.
Example: Calculator is a very good example
The key expressions are number, addition, and subtraction. We identified the rules. We start with the Expression interface/protocol. There should be an interpret method with no parameters, and it just returns a number. Now that we have the protocol, we can create the number type which conforms to the protocol we defined. And for each addition subtraction multiplication and division, we create an Expression type conforming to the protocol.
The number type is a terminal node and the expression type is a non-terminal node. In the non-terminal, there will LHS and RHS which gives us terminal result via their interpret method. Using these 2 results we calculate the expression and return the result in expression’s interpret method.
Like the calculator example, we should map the domain to a language with a small set of rules. In other words, the grammar must be simple. If we can’t express the domain with a relatively small set of rules, the interpreter pattern might not be a good fit to solve that particular problem.
The disadvantage of the interpreter is that it can become complicated. The number of required types increases as the language becomes more verbose. This complexity can lead to maintenance and performance issues. Besides, the interpreter does not address parsing; thus, other approaches, like for example a parser generator, may be a better choice. The next pattern we’re going to look at is the iterator. It is one of the most popular patterns, and we use it even without thinking about it.