Design Pattern: The Interpreter Pattern
The Interpreter Pattern is a pattern that can be used when you have a very contained, specific problem or set of problems—and it may work best to express the solution to that problem in a language created specifically to solve that problem.
Although I can understand how it’s used, I honestly can’t think of a solid example beyond those offered up by others, so I’ll start with those.
In Design Patterns in Ruby Olsen starts with the basic example of creating an interpreter that can interpret math symbols and numbers (a very contrived example because of course Ruby can already handle numbers and math symbols), and then moves on to an interpreter for searching out certain types of files. Olsen creates different classes that each interpret a different command that relates to what type of files you want to search for.
For any Java fans, I also came across this example of a Roman numerals interpreter, which the site referred to as “the classical example of the interpreter pattern”. Last but not least, there’s even an example using musical notation.
How it works
There are really two main steps to the Interpreter Pattern:
1) A parser* will read the input and produce an Abstract Syntax Tree. The nodes of the tree are separated into terminal and nonterminal nodes. The nonterminal nodes are higher level concepts (in the case of math, a plus or multiplication sign, and terminal nodes are the most objects that won’t be broken down any further: a number, a variable, etc.
2) Once the Abstract Syntax Tree is done, then the nodes can be evaluated against whatever conditions you’ve set up…. and then you will have your result. In his file finder example Olsen uses conditions like files of type “mp3” or files smaller than 1 Megabyte.
A Plus and A Minus
There is one big plus and one big minus to this pattern:
1) The plus is that once you’ve gone through the work of setting up your parser and separate classes of expressions you have a lot of flexibility with your interpreter. It can be a powerful tool for your specific set of problems.
2) The downside is that there is a performance hit when the machine has to go through the steps of setting up the abstract syntax tree and then starts to evaluate and take action. However, with the speed of computers nowadays this performance hit will be negligible, but there will be times you’ll have to be aware of it.
Ruby itself is of course an interpreted language, but not implemented in quite the same way and well beyond the scope of the interpreter pattern. For now the pattern will be one of those that I keep aclose eye out for in the wild so that I can find some more concrete examples of it.
- there is also a parser-less interpreter but I’m not going to go into it here so that I can keep the primary one clear.