Monday, September 9, 2024

Interpreter Design Pattern in C#

The Interpreter Design Pattern is a behavioral design pattern that defines a grammatical representation for a language and provides an interpreter to deal with this grammar. This pattern is particularly useful for designing simple languages or interpreting expressions.

When to Use the Interpreter Pattern

  • When you have a simple language to interpret.
  • When you need to interpret expressions in a language.
  • When the grammar of the language is relatively simple and stable.

Components of Interpreter Design Pattern

The Interpreter pattern involves the following components:

  1. Abstract Expression: Declares an abstract Interpret method.
  2. Terminal Expression: Implements the Interpret method for terminal symbols in the grammar.
  3. Non-Terminal Expression: Implements the Interpret method for non-terminal symbols in the grammar.
  4. Context: Contains information that’s global to the interpreter.

Example in C#: Simple Arithmetic Interpreter

Let’s create a simple arithmetic interpreter that can evaluate expressions like “3 + 5 - 2”.

Define the Abstract Expression
namespace InterpreterPattern
{
    /// <summary>
    /// Abstract Expression
    /// </summary>
    public abstract class Expression
    {
        public abstract int Interpret();
    }
}
Define Terminal Expressions
namespace InterpreterPattern
{
    /// <summary>
    /// Terminal Expression
    /// </summary>
    public class NumberExpression : Expression
    {
        private int _number;

        public NumberExpression(int number)
        {
            _number = number;
        }

        public override int Interpret()
        {
            return _number;
        }
    }
}
Define Non-Terminal Expressions
// Add Expression
namespace InterpreterPattern
{
    /// <summary>
    /// Non-Terminal Expression
    /// </summary>
    public class AddExpression : Expression
    {
        private Expression _leftExpression;
        private Expression _rightExpression;

        public AddExpression(Expression leftExpression, Expression rightExpression)
        {
            _leftExpression = leftExpression;
            _rightExpression = rightExpression;
        }

        public override int Interpret()
        {
            return _leftExpression.Interpret() + _rightExpression.Interpret();
        }
    }
}

//Subtract Expression
namespace InterpreterPattern
{
    /// <summary>
    /// Non-Terminal Expression
    /// </summary>
    public class SubtractExpression : Expression
    {
        private Expression _leftExpression;
        private Expression _rightExpression;

        public SubtractExpression(Expression leftExpression, Expression rightExpression)
        {
            _leftExpression = leftExpression;
            _rightExpression = rightExpression;
        }

        public override int Interpret()
        {
            return _leftExpression.Interpret() - _rightExpression.Interpret();
        }
    }
}
Define the Context
namespace InterpreterPattern
{
    /// <summary>
    /// Context
    /// <summary>
    public class Context
    {
        private Dictionary<string, int> _variables;

        public Context()
        {
            _variables = new Dictionary<string, int>();
        }

        public int GetVariable(string name)
        {
            return _variables[name];
        }

        public void SetVariable(string name, int value)
        {
            _variables[name] = value;
        }
    }
}
Client Code(Program.cs)
// Example: (3 + 5) - 2
using InterpreterPattern;

Expression expression = new SubtractExpression(
    new AddExpression(
        new NumberExpression(3),
        new NumberExpression(5)
    ),
    new NumberExpression(2)
);

Console.WriteLine($"Result: {expression.Interpret()}");
Console.WriteLine();

Explanation

  1. NumberExpression: Represents numbers in the expression.
  2. AddExpression: Represents addition operations.
  3. SubtractExpression: Represents subtraction operations.
  4. Context: Holds any global information needed by the interpreter (not used in this simple example).
  5. InterpreterClient: Constructs the expression tree and interprets it.

Conclusion

The Interpreter Design Pattern is a powerful tool for evaluating expressions defined by a formal grammar. While this example demonstrates arithmetic operations, the pattern can be extended to handle more complex languages and operations.

By structuring your code using the Interpreter pattern, you can keep the logic for interpreting each type of expression encapsulated within its own class, leading to a clean and maintainable design.

The full source code is available here:

Interpreter Design Pattern in C#

Happy coding!! 😊

No comments:

Post a Comment

^ Scroll to Top