Back to: Design Patterns in C# With Real-Time Examples
Interpreter Design Pattern in C#
In this article, I will discuss the Interpreter Design Pattern in C# with Examples. Please read our previous article discussing the Strategy Design Pattern in C# with Examples. The Interpreter Design Pattern falls under the category of Behavioral Design Pattern. As part of this article, we will discuss the following pointers in detail.
- What is the Interpreter Design Pattern?
- Understanding the Class Diagram of Interpreter Design Pattern
- Implementing the Interpreter Design Pattern in C#.
- When to use the Interpreter Design Pattern?
- Real-Time Examples of Interpreter Design Patterns
- Advantages and Disadvantages of Interpreter Design Pattern
What is the Interpreter Design Pattern?
The Interpreter Design Pattern is a Behavioral Design Pattern that defines a grammatical representation of a language and provides an interpreter to deal with this grammar. The main idea is to define a domain language (a small language specific to your application) and interpret expressions in that language. It is useful when interpreting sentences in a language according to a defined grammar.
That means the Interpreter Design Pattern Provides a way to evaluate language grammar or expression. This pattern is used in SQL Parsing, Symbol Processing Engines, etc.
Example to Understand Interpreter Design Pattern
Let us understand the Interpreter Design Pattern with an example. Please have a look at the following image. On the left-hand side, you can see the Context. The Context is nothing but the value that we want to interpret. Here, the context value is the current date. On the right-hand side, you can see the Date expression, or you can say the grammar. We have different types of date expressions, such as (MM-DD-YYYY, DD-MM-YYYY, YYYY-MM-DD, and DD-YYYY).
Suppose you want the date in MM-DD-YYYY format; then, you must pass the Context value and the Date Expression you want (i.e., MM-DD-YYYY) to the interpreter. What the interpreter will do is it will convert the context value into the date expression format you passed to it. So, the interpreter contains the logic or grammar to convert the context object into a specific readable format.
How to Implement Interpreter Design Pattern in C#?
The Interpreter pattern provides a way to evaluate language grammar or expressions for particular languages, mostly for domain-specific languages. This pattern involves breaking down a task into small expressions that can be interpreted by specialized objects created for each expression. Let’s break down the design pattern:
- AbstractExpression: Declares an abstract Interpret operation common to all nodes in the abstract syntax tree.
- TerminalExpression: Implements the Interpret operation associated with terminal symbols in the grammar. It’s an instance of AbstractExpression.
- NonTerminalExpression: One level of grammar expression. For grammar rules that require multiple instances of AbstractExpression, you’d use the NonTerminalExpression.
- Context: Contains global information and is typically defined outside the grammar.
- Client: Builds (or is provided) the abstract syntax tree representing a particular sentence in the grammar. The tree is then evaluated by invoking the Interpret operation.
Let us implement the example we discussed here using the Interpreter Design Pattern in C#. So, here, we need to convert the DateTime to a specific format. To achieve this, we can define different types of grammar. Please have a look at the following diagram. As you can see in the below image, we define a class for each type of grammar, such as Month, Year, Day, and separator. So, using this grammar, you can create any date format.
Step 1: Creating Context
This class will contain information (Input and Output) that the Interpreter will use. Create a class file named Context.cs and copy and paste the following code. This class contains the date (i.e., current date-time) we want to interpret. The following Context class code is self-explained, so please go through the comment lines for a better understanding.
using System; namespace InterpreterDesignPattern { //This is a class that contains information (Input and Output) that is going to be used by the Interpreter. public class Context { //The Expression Property is going to hold the Output public string Expression { get; set; } //The Date Property is going to hold the Input public DateTime Date { get; set; } //While Creating the Context Object, we need to send the Input data public Context(DateTime date) { //Initializing the Input Date Property through the Constructor input parameter value Date = date; } } }
Step 2: Creating AbstractExpression
Create an interface named IExpression.cs and copy and paste the following code. This class defines the method that the child classes will implement. Moreover, the method takes the Context object as a parameter. This context object holds the value that we want to interpret.
namespace InterpreterDesignPattern { //This is going to be an interface that defines the Interpret operation, which must be implemented by each subclass. public interface IExpression { void Evaluate(Context context); } }
Step 3: Terminal Expressions
These are going to be the concrete classes that implement the IExpression interface. Each class implements the Evaluate method according to its own grammar.
DayExpression:
Create a class file named DayExpression.cs, then copy and paste the following code. The Evaluate method replaces the expression DD value with the exact Day value stored in the context object.
namespace InterpreterDesignPattern { //This is going to be a Concrete class that implements the Expression Interface. //The following Concrete DayExpression Class evaluates the Day grammar //That is Replacing DD with the Day from the Input Date Property public class DayExpression : IExpression { public void Evaluate(Context context) { string expression = context.Expression; context.Expression = expression.Replace("DD", context.Date.Day.ToString()); } } }
MonthExpression:
Create a class file named MonthExpression.cs, then copy and paste the following code. The Evaluate method replaces the expression MM value with the exact Month value that is stored in the context object.
namespace InterpreterDesignPattern { //This is going to be a Concrete class that implements the Expression Interface. //The following Concrete MonthExpression Class evaluates the Month grammar //That is Replacing MM with the Month from the Input Date Property public class MonthExpression : IExpression { public void Evaluate(Context context) { string expression = context.Expression; context.Expression = expression.Replace("MM", context.Date.Month.ToString()); } } }
YearExpression:
Create a class file named YearExpression.cs, then copy and paste the following code. The Evaluate method replaces the expression YYYY value with the exact Year value that is stored in the context object.
namespace InterpreterDesignPattern { //This is going to be a Concrete class that implements the Expression Interface. //The following Concrete YearExpression Class evaluates the Year grammar //That is Replacing YYYY with the Year from the Input Date Property public class YearExpression : IExpression { public void Evaluate(Context context) { string expression = context.Expression; context.Expression = expression.Replace("YYYY", context.Date.Year.ToString()); } } }
SeparatorExpression:
Create a class file named SeparatorExpression.cs, then copy and paste the following code. The Evaluate method replaces the space with a hyphen (-) separator.
namespace InterpreterDesignPattern { //This is going to be a Concrete class that implements the Expression Interface. //The following Concrete SeparatorExpression Class evaluates the separate grammar //That is Replacing space with the - in the Expression string which is going to be our output class SeparatorExpression : IExpression { public void Evaluate(Context context) { string expression = context.Expression; context.Expression = expression.Replace(" ", "-"); } } }
Step 4: Client
The Main method of the Program class is going to be the Client. So, modify the Main method of the Program class as shown below. Here, we will ask the user to select the date format, and then we will display the date as per the user-selected date format. The following Client code is self-explained, so please go through the comment lines for a better understanding.
using System; using System.Collections.Generic; namespace InterpreterDesignPattern { //This is the class that builds the abstract syntax tree for a set of instructions in the given grammar. //This tree builds with the help of instances of NonTerminalExpression and TerminalExpression classes. class Program { static void Main(string[] args) { //The following is going to be our Expression Tree List<IExpression> objExpressions = new List<IExpression>(); //Creating the context object by passing the current date-time value Context context = new Context(DateTime.Now); //We want to Interpret the current date time as a specific format //Ask the user to select the format Console.WriteLine("Please Select the Expression : MM DD YYYY or YYYY MM DD or DD MM YYYY "); context.Expression = Console.ReadLine(); //Split Expression which the user selects to an array so that we can apply different Expression rules string[] strArray = context.Expression.Split(' '); //Looping through Each Element of the Expression and adding the Appropriate Expression with the Expression Tree foreach (var item in strArray) { if (item == "DD") { objExpressions.Add(new DayExpression()); } else if (item == "MM") { objExpressions.Add(new MonthExpression()); } else if (item == "YYYY") { objExpressions.Add(new YearExpression()); } } //Adding the SeparatorExpression objExpressions.Add(new SeparatorExpression()); foreach (var obj in objExpressions) { //Finally Evaluate Each Expression which is added in the Expression Tree obj.Evaluate(context); } //Print the Expression as Output Console.WriteLine(context.Expression); Console.Read(); } } }
Output:
Use Cases of Interpreter Design Pattern:
- When you have a language to interpret, you can represent statements in this language as abstract syntax trees.
- In scenarios like SQL parsing, symbol processing engines, or the transformation of complex data formats.
Understanding the Class or UML Diagram of Interpreter Design Pattern:
Let us understand the Class Diagram or UML Diagram of the Interpreter Design Pattern and the components involved. Please have a look at the following image.
As shown in the above UML or Class Diagram, five participants are involved in the Interpreter Design Pattern in C#. Their role and responsibilities are as follows:
- Context: This is a class that contains information (Input and Output) that is going to be used by the Interpreter to Interpret. In our example, it is the Context class.
- AbstractExpression: This interface defines the interpretation method that the subclasses should implement. This method takes the context object as a parameter. This context object holds the data that we want to interpret. In our example, it is the IExpression Interface.
- TerminalExpression: This is a concrete class that implements the AbstractExpression interface. The TerminalExpression represents elements in the grammar that do not get replaced, such as symbols. In our example, we are not using NonTerminalExpression.
- NonTerminalExpression: This is also a concrete class that implements the AbstractExpression interface. The NonTerminalExpression represents elements that will be replaced during the evaluation, such as variables or rules. In our example, it is the DayExpression, MonthExpression, YearExpression, and SeparatorExpression classes.
- Client: This class builds the abstract syntax tree for instructions in the given grammar. This tree builds with the help of instances of NonTerminalExpression and TerminalExpression classes. In our example, it is the Main method of the Program class.
Advantages of Interpreter Design Pattern:
- Easy to Change and Extend Grammars: Grammars can be changed or extended by creating new expressions.
- Separation of Concerns: Separates the grammar definition and interpretation logic from the main application logic.
- Reusable Components: Individual grammar rules can be reused across the application.
When to Use Interpreter Design Pattern in C#?
The Interpreter Design Pattern in C# is particularly useful in scenarios where:
- Implementing Domain-Specific Languages (DSLs): If you are creating a domain-specific language, the Interpreter pattern can be used to interpret the sentences in the language. This is common in scripting languages, configuration languages, or specialized computer languages.
- Parsing and Interpreting Expressions: When you need to parse and interpret expressions, such as boolean expressions or mathematical formulas. The pattern can represent grammar rules as classes and interpret these expressions accordingly.
- Syntax Trees for Expressions: In scenarios where you need to build a syntax tree for an expression and then evaluate that expression. Each node in the tree represents a construct in the language, and the tree is used to interpret the expression.
- Rule-Based Systems: If you’re developing rule-based systems where rules are defined in a language that needs to be interpreted to execute actions based on those rules.
- SQL Query Parsing: For parsing and interpreting SQL queries or other complex queries in software. The Interpreter pattern can be used to process these queries into understandable actions or operations.
- Simple Scripting Engines: When building simple scripting engines or command processors that execute based on textual commands or scripts.
- Compilers and Interpreters: In the development of compilers and interpreters, especially for simpler or domain-specific languages.
- Regular Expressions Evaluation: When implementing systems that need to evaluate or process regular expressions, the Interpreter pattern can be used to represent and interpret the regular expressions.
In the next article, I will discuss the Real-Time Examples of the Interpreter Design Pattern in C#. In this article, I try to explain the Interpreter Design Pattern in C# with Examples. I hope you understand the need and use of the Interpreter Design Pattern in C# with Examples.
and this code is true:
//foreach (var item in strArray)
//{
// if (item == “DD”)
// {
// objExpressions.Add(new DayExpression());
// }
// else if (item == “MM”)
// {
// objExpressions.Add(new MonthExpression());
// }
// else if (item == “YYYY”)
// {
// objExpressions.Add(new YearExpression());
// }
//}
objExpressions.Add(new YearExpression());
objExpressions.Add(new MonthExpression());
objExpressions.Add(new DayExpression());
objExpressions.Add(new SeparatorExpression());
foreach (var obj in objExpressions)
{
obj.Evaluate(context);
}