In software applications, situations often arise where we need to interpret a simple language, an expression, or a set of rules: filters, mathematical formulas, validation rules, search expressions, or even DSLs (Domain Specific Languages). Interpreter Pattern is a behavioral design pattern that offers an elegant solution for such scenarios.
In this article, we will see what Interpreter Pattern is, when to use it, advantages and limitations, as well as a clear example in C# (.NET).
What is Interpreter Pattern?
Interpreter Pattern defines a representation for the grammar of a language and a mechanism that allows interpreting expressions in that language. Each rule in the grammar is represented by a class, and the evaluation of the expression is done by composing these objects.
In other words, instead of writing a complex parser in a single class, you split the language rules into small, reusable objects.
π The basic idea:
βAn expression is an object that knows how to interpret itself.β
When is Interpreter Pattern useful?
Interpreter Pattern is suitable when:
-
You have a simple language with well-defined rules
-
Expressions are relatively stable but can be combined
-
You need dynamic evaluation (at runtime)
-
You want to avoid very complex if / else or switch statements
-
You are building a DSL for a specific domain
Real examples:
-
Mathematical expressions (1 + 2 - 3)
-
Business rules (age > 18 AND country == "RO")
-
Search filters
-
Rule engines
-
Logical expressions for permissions
Pattern structure
Interpreter Pattern contains several key elements:
-
Expression (Abstract Expression) β the common interface
-
TerminalExpression β terminal expressions (concrete values)
-
NonTerminalExpression β composite expressions (operations)
-
Context β information needed for interpretation
Practical example in C# (.NET)
Let's implement a simple interpreter for mathematical expressions.
1οΈβ£ Expression interface
public interface IExpression
{
int Interpret();
}
2οΈβ£ TerminalExpression β concrete values
public class NumberExpression : IExpression
{
private readonly int _number;
public NumberExpression(int number)
{
_number = number;
}
public int Interpret()
{
return _number;
}
}
3οΈβ£ NonTerminalExpression β operations
Addition
public class AddExpression : IExpression
{
private readonly IExpression _left;
private readonly IExpression _right;
public AddExpression(IExpression left, IExpression right)
{
_left = left;
_right = right;
}
public int Interpret()
{
return _left.Interpret() + _right.Interpret();
}
}
Subtraction
public class SubtractExpression : IExpression
{
private readonly IExpression _left;
private readonly IExpression _right;
public SubtractExpression(IExpression left, IExpression right)
{
_left = left;
_right = right;
}
public int Interpret()
{
return _left.Interpret() - _right.Interpret();
}
}
4οΈβ£ Using the interpreter
class Program
{
static void Main()
{
// (5 + 3) - 2
IExpression expression = new SubtractExpression(
new AddExpression(
new NumberExpression(5),
new NumberExpression(3)
),
new NumberExpression(2)
);
Console.WriteLine(expression.Interpret()); // Output: 6
}
}
βοΈ Each expression knows how to interpret itself
βοΈ Composition is flexible
βοΈ Code is extensible
Advantages
β Clear and structured code
β Easy to extend with new rules
β Eliminates complex logic from a single class
β Can build dynamic expressions at runtime
Disadvantages
β οΈ Large number of classes for complex grammars
β οΈ Lower performance for very large expressions
β οΈ Not suitable for complex languages (SQL, JSON, etc.)
π For advanced cases, dedicated parsers are a better solution.
Interpreter Pattern in real .NET
In the .NET ecosystem, the pattern implicitly appears in:
-
Rule engines
-
Validation frameworks
-
Filtering systems
-
LINQ expressions (conceptually)
Often you use it without realizing it, in the form of composed expressions.
Conclusion
Interpreter Pattern is an elegant solution for interpreting expressions and rules in an extensible and clear way. It is not a universal pattern, but when used correctly, it reduces complexity and increases code readability.