05 Dec 2023



Advanced

The Interpreter design pattern is a behavioral pattern that defines a grammar for interpreting language expressions and provides an interpreter to interpret sentences in that language. In C#, the Interpreter pattern typically involves the following main components:

  1. Abstract Expression:

    • Declares an interface for interpreting an expression.
    • Usually, this interface includes an Interpret method.
    public interface IExpression
    {
        bool Interpret(string context);
    }
    
  2. Terminal Expression:

    • Implements the IExpression interface.
    • Represents a terminal symbol in the grammar (i.e., an atomic element that cannot be further divided).
    public class TerminalExpression : IExpression
    {
        private string terminal;
    
        public TerminalExpression(string terminal)
        {
            this.terminal = terminal;
        }
    
        public bool Interpret(string context)
        {
            return context.Contains(terminal);
        }
    }
    
  3. Non-terminal Expression:

    • Also implements the IExpression interface.
    • Represents a non-terminal symbol in the grammar, usually consisting of multiple terminal and/or non-terminal expressions.
    public class AndExpression : IExpression
    {
        private IExpression expression1;
        private IExpression expression2;
    
        public AndExpression(IExpression expression1, IExpression expression2)
        {
            this.expression1 = expression1;
            this.expression2 = expression2;
        }
    
        public bool Interpret(string context)
        {
            return expression1.Interpret(context) && expression2.Interpret(context);
        }
    }
    
    public class OrExpression : IExpression
    {
        private IExpression expression1;
        private IExpression expression2;
    
        public OrExpression(IExpression expression1, IExpression expression2)
        {
            this.expression1 = expression1;
            this.expression2 = expression2;
        }
    
        public bool Interpret(string context)
        {
            return expression1.Interpret(context) || expression2.Interpret(context);
        }
    }
    
  4. Context:

    • Contains information that needs to be interpreted.
    • Often not explicitly used in the basic Interpreter pattern but may be passed to the Interpret method.
    public class Context
    {
        private string input;
    
        public Context(string input)
        {
            this.input = input;
        }
    
        public string Input
        {
            get { return input; }
        }
    }
    
  5. Client:

    • Builds the abstract syntax tree of expressions.
    • Provides the context and invokes the Interpret method to interpret the expression.
    class Client
    {
        static void Main()
        {
            IExpression expression1 = new TerminalExpression("hello");
            IExpression expression2 = new TerminalExpression("world");
            IExpression andExpression = new AndExpression(expression1, expression2);
    
            IExpression expression3 = new TerminalExpression("foo");
            IExpression expression4 = new TerminalExpression("bar");
            IExpression orExpression = new OrExpression(expression3, expression4);
    
            Context context = new Context("hello world");
    
            bool result1 = andExpression.Interpret(context.Input);
            bool result2 = orExpression.Interpret(context.Input);
    
            Console.WriteLine($"AND expression result: {result1}");
            Console.WriteLine($"OR expression result: {result2}");
        }
    }
    

In this example:

  • IExpression is the abstract expression interface that declares the Interpret method.
  • TerminalExpression is a concrete terminal expression class that interprets a single symbol or atomic element.
  • AndExpression and OrExpression are concrete non-terminal expression classes that interpret combinations of expressions.
  • Context is an optional class that contains the information to be interpreted.
  • The Client builds the abstract syntax tree of expressions, provides the context, and invokes the Interpret method to interpret the expression.

The Interpreter pattern is useful when you have a grammar for a language, and you want to build an interpreter to interpret sentences in that language. It allows you to define the grammar using a set of classes representing expressions and provides a way to evaluate those expressions.

software-design-patterns
interpreter-design-pattern
c#