Runtime flexibility and future proofing; GOF BEHAVIOURAL Patterns with C# - part one - by Barry Mossman

By: Barry Mossman

Abstract: This is the third article in a series upon the GOF Design patterns from a C# and .Net Framework perspective. It examines some of the BEHAVIORAL Patterns; Chain of Responsibility, Command and Interpreter.

Runtime flexibility and future proofing; GOF Behavioural Patterns with C# - part 1

BarryMossman<is_at>primos.com.au

This is my third article about the GOF Design patterns. The first two articles studied the Creational and Structural Patterns, while this one begins to look at the Behavioural Patterns. There are links to my first two articles at the bottom of this one.

There is too much material within the Behavioural patterns to be covered by one article, so I cover just the first three patterns in this article, and will mop up the remainders in a follow ups. This articles covers the following patterns:

  • Chain of Responsibility, Command, Interpreter

The following Behavioural patterns have been deferred to follow up articles:

  • Iterator, Mediator, Observer, Memento, State, Strategy, Template Method, Visitor

The source for this article's demonstration program is available at CodeCentral. See the link at the bottom of this document.

The first article (Creational Patterns) also gave an overview of where the patterns came from, and the general discussion of the general techniques that they promoted. I will firstly briefly recap a few of the points from that article.

The GOF design patterns help address the following challenges :

    • design ready to accommodate change & growth

    • design flexible systems that come ready to handle reconfiguration and run time tailoring

    • code in manner to facilitate reuse during the development and extension phases ... ie. both external and internal reuse, so that we are rewarded by efficiencies as the project progresses, coming for investments made earlier in the project.

    • implement change in a way that doesn't overly shorten the system's useful lifespan

In a multi-person project the design patterns have the additional utility of providing a shorthand language with which to describe design options and specifications.

The design patterns were defined in the programming classic entitled Design Patterns by Gamma, Helm, Johnson & Vlissides. The four authors are commonly described as the Gang Of Four (GOF) for brevity. The subtitle of the book is elements of reusable object-oriented software. There is a link to this book at the end of this document.

One of the book's key points is that the authors favour using object composition over class inheritance when designing our system's objects. This means that our objects are assembled at runtime from a number of helper classes working together to deliver the desired behaviour, rather than being statically defined at compile time using class inheritance. Objects that are created in this way offer a great deal of runtime flexibility, and are better set up for future modification. A general theme of the Behavioural patterns is that they allow the composition of larger and more flexible structures from smaller helper classes.

The approach necessitates more physical classes, but is made more workable where patterns are used as they help to bring an understandable structure to the design. The fact that the patterns are based upon system design structures that have been tested and refined over time is also of assistance.

Note that the advice is to only to favour object composition over class inheritance, not to stop using inheritance altogether. The two techniques work well together



A summary of the patterns

Pattern Name

General Objective

Chain of Responsibility

Allows us to decouple a client's request for action, from the class that will implement it.

The client builds a chain of candidate classes (Handlers) to handle the request, and then passes the request to the chain. The client is simplified as it does not need to know which class will finally handle the request. The request is passed down the chain, one handler at a time, until one of them accepts responsibility for the request.

There is much flexibility as the chain is built at runtime, and the sequence can therefore be tailored by runtime or configuration conditions.

Command

Allows us to make commands, or requests against an object, into objects themselves.

Potential OO benefits include grouping of command series into macros, providing undo support, facilitating command logging, persisting commands or macros via Save|Restore, queuing commands for later execution, and runtime tailoring of commands that are to be executed.

Interpreter

This pattern enables us to design a script language, it's syntax, and then implement an Interpreter to process requests that have been recorded in that language.

This allows us to create a flexible client that is capable of receiving and actioning high level request scripts written in a command language that we have designed to suit the users of our system.



Chain of Responsibility Pattern

This pattern allows us to decouple a client's request for action, from the class that will implement it. We have seen something similar in the Bridge pattern (Structural patterns). In the case of the Bridge pattern the client decided at runtime which Implementor class was going to handle calls to the Adapter's interface. This gave us runtime flexibility, but the client needed to be involved in choosing which Implementor class was best to handle the request.

In the Chain of Responsibility pattern the client sets up a chain of candidate Implementors (called Handlers in this pattern). The request is passed to the chain, where it bubbles up, until one of the Handlers determines that it should handle the request itself. The client is simplified as it does not need to know which class will finally handle the request. It just passes the request to the object that implements the chain pattern, and leaves the chain to sort out who should handle things.

A familiar example of this kind of design is when an Exception is raised in the client. The exception bubbles up the invocation stack until somebody handles it. The client doesn't need to worry about who should handle the exception. It can trust that somebody appropriate will handle it, even if it is just the default handler in the dotNet framework.

The design is flexible as the chain is built, and can be re-configured, at runtime.

The Handlers are relatively simple as they don't need to know about the client, nor do they need to know about other handlers in the chain other than their immediate successor. However the chain does need to be set up correctly to ensure that some Handler will pick up responsibility for the request, and that it won't just fall off the end of the chain.

The chain could use existing relationships between the Handlers if such relationships exist(eg. Parent references if there were nodes with a Composite pattern (see Structural patterns), or the client can define it's own set of relationships between the handlers in the chain.

Our demonstration program simulates a dynamic requirement by passing the current time into the chain of handlers. The handler that will accept responsibility is dependant how far through the current minute we have progressed.

Firstly our client code is as follows:

        /* Set up a chain of candidate handlers to handle the request. Chain
           will be (1st considered -> last): ConcreteHandler1,
           ConcreteHandler2, ConcreteHandler3. */
        ShowUserCommentary(1);
        HandlerBase chain = new ConcreteHandler3();
        HandlerBase more = new ConcreteHandler2();
        more.Successor = chain;
        chain = new ConcreteHandler1 ();
        chain.Successor = more;
        // hand the request to the chain
        listBox1.AppendText(chain.SayWhen(DateTime.Now));

This produces the following output (as with all of the following examples the black text is output by the ShowUserCommentary method, and the blue text is output by the pattern).:




Now to the implementation, the essential interface for the pattern could be described as follows:

        public interface IHandler {
                // properties
              HandlerBase Successor { set; }
        }

Firstly we need to define an abstract base class for the Handlers. This is where we implement the Successor property:

        // --- Abstract Handler
        abstract public class HandlerBase {
                // fields
                protected HandlerBase _successor;
                // properties
                public HandlerBase Successor {
                        set { _successor = value; }
                }
                // methods
                abstract public string SayWhen(DateTime localTime);
        }

Finally we have the implementation of the Handlers. Here are the first two:

       // --- Concrete handlers
        public class ConcreteHandler1: HandlerBase {
                // methods
                override public string SayWhen(DateTime localTime) {
                        if (localTime.Second < 15)
                                return String.Format(I am {0}.nThe time is {1:T}nWe are just starting on a shiny new minute. Just think of all the things you could do with it!,
                                        this.ToString(),localTime);
                        else {
                                if (_successor != null) {
                                        return _successor.SayWhen(localTime);
                                }
                                else
                                        throw new ApplicationException(ChainOfResponsibility object exhaused all successors without call being handled.);
                        }
                }
        }

        public class ConcreteHandler2: HandlerBase {
                // methods
                override public string SayWhen(DateTime localTime) {
                        if ((localTime.Second >= 15) & (localTime.Second < 45))
                                return String.Format(I am {0}.nThe time is {1:T}nWe are into the middle half of this minute. You need to get started if you going to use it well.,
                                        this.ToString(),localTime);
                        else {
                                if (_successor != null) {
                                        return _successor.SayWhen(localTime);
                                }
                                else
                                        throw new ApplicationException(ChainOfResponsibility object exhaused all successors without call being handled.);
                                }
                        }
        }                     

Command Pattern

The Command pattern allows us to make a request into an object. The target of the request is called the Receiver, and it knows how to carry out the steps involved in servicing the request. The client wants to have the request serviced, but need not know the steps involved, nor have any knowledge of the Receiver's interface. The client creates a Command object which contains the request type, any parameters, and a reference to the target Receiver. This Command object contains the detailed knowledge of the steps involved in servicing the client's request, and has knowledge of the Receiver's interface.

The pattern can also have an optional object called the Invoker which instructs the command to execute it's request. This could be a menu item or some other GUI control.

Macro Commands can be created which contain a collection of other Command objects.

Possible benefits from use of the command pattern include:

  • decouple the client from the request as well as the object that will be handling it.

  • ability to queue up requests for action at a later time

  • ability to log the requests away for possible re-application after returning the system to a backup check point following a crash

  • ability to bundle up a series of plodding detailed requests into a larger transaction (macro) that makes the business intention clearer

  • provide undo support

  • flexibility where we can provide some runtime configuration to the commands that are issued

  • allow transfer of the command to a different process for handling

The client will be simplified if it can request simple chunky commands that relate to business transactions. The system design intent can be more easily seen if the plodding internal detail for each transaction type is outsourced to the Command objects. It is also reasonably straight forward to add further business transaction types by creating new Command classes. This will involve minimal change requirement inside the client.

My demonstration program shows the following:

  • an individual command is executed

  • then a macro command set, with undo support, is executed and undone

  • the macro command set is persisted to disk

  • the macro command set is reloaded, and applied to a different Receiver.

Here is the client:

        // Create a Receiver, then a Command to add 100.00 to it.
        // Execute the command, and show the result.
        ShowUserCommentary(1);
        Receiver receiver = new Receiver();
        // note: the m suffix causes a literal of type Decimal
        CommandBase command = new CreditCommand(100m,receiver);
        command.Execute();
        listBox1.AppendText(String.Format(nValue is {0},receiver.Value));

        // Create a 2nd receiver. Then create a macro Command set that adds
        // 50.00 to the 2nd receiver, and then subtracts 20.00 from the 1st
        // and then increases the 1st by 5%. Execute the macro.
        ShowUserCommentary(2);
        Receiver receiver2 = new Receiver();
        MacroCommand macro = new MacroCommand();
        command = new CreditCommand(50m,receiver2);
        macro.Add(command);
        command = new DebitCommand(20m,receiver);
        macro.Add(command);
        command = new PercentIncreaseCommand(5m,receiver);
        macro.Add(command);
        macro.Execute();
        listBox1.AppendText(String.Format(
                nValue 1st receiver is now {0}, and 2nd receiver is {1}
                ,receiver.Value, receiver2.Value));

        // Undo the effects of the above macro.
        ShowUserCommentary(3);
        macro.Undo();
        listBox1.AppendText(String.Format(
                nValue 1st receiver is now {0}, and 2nd receiver is {1}
                ,receiver.Value, receiver2.Value));

        // Persist the macro command to disk.
        macro.Save(MacroSave.bin);

        // Reload the macro, and then execute all of the commands against the
        // second Receiver.
        ShowUserCommentary(4);
        MacroCommand macro2 = macro.Load(MacroSave.bin, receiver2);
        macro2.Execute();
        listBox1.AppendText(String.Format(
                nValue 1st receiver is now {0}, and 2nd receiver is {1}
                ,receiver.Value, receiver2.Value));     




The essential public interface for the pattern could be described as:

        public interface ICommand {
                // methods    
               void Execute();
        }

Here is the abstract ancestor for the Commands and Macro class.

You may notice the Serializable attribute which I will discuss when showing the implementation for the Macro Command at the end of this section

.

        // --- Abstract Command   
        [Serializable]
        public abstract class CommandBase {
                // fields
                internal Receiver _receiver;
                internal decimal _amount;
                //properties
                public virtual Receiver TargetReceiver {
                     set {_receiver = value;}
                }
                // constructor
                public CommandBase (decimal aAmount, Receiver aReceiver) {
                        _receiver = aReceiver;
                        _amount = aAmount;
                }
                protected CommandBase () { // only by the MacroBase descendant
                }
                // methods
                abstract public void Execute();
                abstract public void Undo();
        }

Next comes the concrete implementation of the credit command. There are similar implementations for the debit and percentage increase commands which have been omitted for brevity. The Command class provides an interface, but doesn't actually know the details of how to achieve the request's intentions. It is however familiar with the Receiver, and knows which Receiver members to employ to achieve the desired result.

        // --- Concrete Commands
        [Serializable]
        public class CreditCommand: CommandBase {
                // constructor
                public CreditCommand(decimal aAmount, Receiver aReceiver):
                                        base (aAmount, aReceiver) {}
                // methods
                override public void Execute() {
                        _receiver.UpdateValue(ReceiverAction.add,_amount);
                }
                override public void Undo() {
                        _receiver.UpdateValue(ReceiverAction.subtract,_amount);
                }
        }

Then we should look at the Receiver. This is the class that knows the mechanics of how to action the request.

       // --- Receiver
        public enum ReceiverAction {add,subtract,percentIncrease,percentDecrease}
                           
        [Serializable]
       public class Receiver {
             // fields
             decimal _value;
             // properties
                public decimal Value {
                     get { return _value; }
             }
             // methods
             internal void UpdateValue (ReceiverAction aOperation,decimal aAmount) {
                     switch (aOperation) {
                          case ReceiverAction.add:
                               _value += aAmount;
                               break;
                          case ReceiverAction.subtract:
                               _value -= aAmount;
                               break;
                          case ReceiverAction.percentIncrease:
                               _value *= (1+aAmount/100);
                               break;
                          case ReceiverAction.percentDecrease:
                               _value /= (1+aAmount/100);
                               break;
                          default:
                               throw new ApplicationException(Invalid operation);
                          }
                     }
                 }

The above pieces are all that we need to enable our client to execute individual commands and to provide undo support. Now lets us look at providing macro support and persistence of the commands to disk.

The dotNet framework makes it relatively simple to implement the persistence, and retrieval, of our commands as it it has extensive inbuilt support for Serialization. Serialization is where an in-memory object is flattened out to a form that can be transmitted or stored. Deserialization is the reverse process.

We are given two flavours of serialization with dotNet; internally using either XML or a binary format. XML Serialization will handle our objects public fields and properties, but for this task we need all the type information, methods, private fields etc so we will use the binary format option.

All we need to do to make a class serializable, is mark it with the Serializable attribute. This will cause the type's meta data to be marked as needing serialization support from the runtime support (CLR)when our class is instantiated.

To implement the macro facility we subclass from our Abstract CommandBase, and then implement the interface's Save and Load functionality as shown below. The TargetReceiver property is used to bind any loaded (deserialize) commands to their new Receiver. The Macro functionality is implemented via the Add, Execute and Undo methods.

        [Serializable]
        public class MacroCommand: CommandBase {
                // fields
                ArrayList _commandStack;
                //properties
                override public Receiver TargetReceiver {
                        set {
                                foreach(CommandBase command in _commandStack) {
                                        command.TargetReceiver = value;
                                }
                        }
                }
                // constructor
                public MacroCommand(): base() {
                        _commandStack = new ArrayList();
                }
                // methods
                override public void Execute() {
                        foreach(CommandBase command in _commandStack) {
                                command.Execute();
                     }
                }
                override public void Undo() {
                     ArrayList commandsReversed = (ArrayList)_commandStack.Clone();
                        commandsReversed.Reverse();
                     foreach(CommandBase command in commandsReversed) {
                                command.Undo();
                     }
                }

                public void Add(CommandBase aCommand) {
                   _commandStack.Add(aCommand);
              }

                public void Save(string aFileName) {
                        IFormatter formatter = new BinaryFormatter();
                     Stream stream = 
                          new FileStream (aFileName,FileMode.Create,
                          FileAccess.Write,FileShare.None);
                     formatter.Serialize(stream,this);
                     stream.Close();
              }

                public MacroCommand Load(string aFileName, Receiver aReceiver) {
                     IFormatter formatter = new BinaryFormatter();
                     Stream stream = 
                          new FileStream(aFileName,FileMode.Open, 
                          FileAccess.Read, FileShare.Read);
                     MacroCommand macro = (MacroCommand)formatter.Deserialize(stream);
                     stream.Close();
                     macro.TargetReceiver = aReceiver;
                        return macro;
                }
    }

You may have noticed that only the MacroCommand class is serialized, but other classes such as Rec and AbstractCommand have also been marked with the Serializable attribute. This is because while it is true that only the MacroCommand is explicitly serialized, the other class are implicitly serialized. This is because the MacroCommand contains references to these other types. See for example:

  • public MacroCommand Load(string aFileName, Receiver aReceiver) {
  • foreach(CommandBase command in _commandStack) {

The Concrete methods such as CreditCommand are not explicitly mentioned, but they need to marked with the serializable attribute also. If you fail to do so the program will compile successfully, but you will get a SerializationException at runtime. This will occur because the actual objects within the macro are of course instances of these concrete classes, and the CLR will not be able to serialize or deserialize them unless they are identified as needing serialization support.

Interpreter Pattern

This pattern allows us to define a command or query language and it's grammar, to create grammatical sentences in that language, and then to interpret the sentences at runtime. This allows us to create a flexible client that is capable of receiving and actioning a request in a script format written in a command language of our own invention. The pattern allows much high level client flexibility as the command script could be obtained at runtime; maybe built or keyed in by the user, or obtained from a configuration file.

The client receives the command sentence as an Expression syntax tree which is built from a combination of terminal and non-terminal nodes to allow nesting within the command sentence's logical structure. The parsing of a command script to build the syntax tree is not part of the Interpreter pattern, although my demonstration program shows an example of this step.

There is an abstract Expression class that is inherited by all tree nodes. This base class has an abstract Interpret method. There is a concrete sub-class of this base for each grammatical rule within the language that we are implementing. The client creates a Context instance and a syntax tree, and then asks the syntax tree to Interpret itself passing in the context instance as an argument. The grammatical composition of the sentence is represented by the tree, and it's layout causes the various sub-classes to fire in the correct sequence to action this specific command. The sub-classes use the context instance parameter as working storage to build and store the result from the command sentence.

The GOF recommend that the although it is quite easy to extend or modify our language's grammar the overall gramme should not be allowed to get too complex.

My demonstration program contains two illustrations:

  • the first implements a simple language that allows the client to build a flexible date based string that would be suitable for use as a archive backup file name

  • the second example allows us to write a query command to interrogate a date known to the context

The first illustration allows the client to build a name for a archive file that we may like to write. The file name is to be assembled from some combination of day, month & year digits, our pattern's namespace name, and a string that we supply at runtime. The pattern will build the string as instructed by the shape of the syntax tree received from the client.

Firstly let us look at client code that will explicitly build and use this syntax tree, using a file name structure that is known at compile time. In this case the file name will be built in the following sequence:

  • current year (2 digits)

  • current month (2 digits)

  • current day (2 digits)

  • a - character

  • finally the namespace name where the pattern is implemented

        ShowUserCommentary(1);
        ArrayList commandList = new ArrayList();
        commandList.Add(new ExpressionCodeYear());
        commandList.Add(new ExpressionCodeMonth());
        commandList.Add(new ExpressionCodeDay());
        commandList.Add(new ExpressionCodeConstantString(-));
        commandList.Add(new ExpressionCodeNamespace());
        Context context = new Context();
        foreach(ExpressionBase exp in commandList)
                exp.Interpret(context);

        /* display the file name assembled by the Interpreter from our syntax
           tree */
        listBox1.AppendText(context.FileName);

This pattern is more powerful if the syntax tree structure is not determined until runtime, after parsing a command script, which could have been obtained from the user, or obtained from a configuration file. In this next example the client parses the string n%-%ymd which will cause the following syntax tree to be built:

  • the namespace name where the pattern is implemented

  • a - character

  • year digits

  • month digits

  • day digits

        /* Now parse a command string to determine how to build the syntax
           tree. This allows flexibility as the command string may have been
           supplied at runtime */
        ShowUserCommentary(2);
        InterpreterParsing parse = new InterpreterParsing();
        string fileNamePattern = n%-%ymd;
        commandList = parse.ParseFileNamePattern(fileNamePattern);
        context = new Context();
        foreach(ExpressionBase exp in commandList)
                exp.Interpret(context);

        listBox1.AppendText(context.FileName);

When we run the test the following output is produced:




The essential interface for the pattern could be described as:

        public interface IContext {     }

        public interface IExpression {
                // methods
                void Interpret(IContext aContext);
        }

I will ignore the parsing step for the moment as this is not really part of the Interpreter pattern. Let us imagine that the Expression tree object has already been built as in the first client snippet, and look at the implementation of the pattern classes that will interpret this tree. The first part of the implementation that we shall look at is the Context which contains working storage used during evaluation of the expression, and also contains the result returned from this evaluation:

    // --- Context
    public class Context {
        // fields
        internal string _fileName = ;
        // properties
        public string FileName {
            get { return _fileName; }
        }
    }

Then there is the definition of a base class for the Expression nodes:

        // --- Abstract Expression
       public abstract class ExpressionBase {
            // methods
            public abstract void Interpret(Context aContext);
       }

And then the concrete Expression classes each of which provides implementation of the abstract Interpret method. I have omitted the Month and Year classes for brevity as they are only slightly different from the day class which is shown:

    // --- Concrete Expressions
    public class ExpressionCodeDay: ExpressionBase {
        // methods
        public override void Interpret(Context aContext) {
            aContext._fileName += String.Format({0:dd},DateTime.Today);
        }
    }
    public class ExpressionCodeNamespace: ExpressionBase {
        // methods
        public override void Interpret(Context aContext) {
            aContext._fileName += String.Format({0},this.GetType().Namespace);
        }
    }
    public class ExpressionCodeConstantString: ExpressionBase {
        // fields
        string _string;
        // constructors
        public ExpressionCodeConstantString(string AString) {
            _string = AString;
        }
        // methods
        public override void Interpret(Context aContext) {
            aContext._fileName += String.Format({0},_string);
        }
    }

Parsing of a command script to build the syntax tree is not actually part of the pattern, but my demonstration program includes this step as this is what makes the pattern powerful. Here is the code that does the parsing. This kind of problem is made quite simple if you employ Regular Expressions engine implemented into dotNet to break apart the string being parsed.

        /*---------------------------------------------------
          Parse a file name expression string and build an Interpreter pattern
          command structure as directed. Expression syntax is some combination
          chosen from the following:
                y  = year
                m  = month
                d  = day
                n  = namespace name
                %s% where s is a string (no embedded blanks)

        So an expression string of ymd%--%n would produce the following
        Interpreter syntax tree:
                ArrayList commandList = new ArrayList();
                commandList.Add(new ExpressionCodeYear());
                commandList.Add(new ExpressionCodeMonth());
                commandList.Add(new ExpressionCodeDay());
                commandList.Add(new ExpressionCodeConstantString(--));
                commandList.Add(new ExpressionCodeNamespace());
        ---------------------------------------------------*/
        public ArrayList ParseFileNamePattern(string aPattern) {
            ArrayList commandList = new ArrayList();

            Regex regex = new Regex(
                  @%.*  # a constant string identified by a leading %
                  (?=%)  # drop off the trailing % so it is discarded
                  |y     # y = year
                  |m     # m = month
                    |d     # d = day
                  |n     # n = namespace,
                  RegexOptions.IgnoreCase
                  | RegexOptions.Multiline
                  | RegexOptions.IgnorePatternWhitespace
                  | RegexOptions.Compiled
                );

                MatchCollection itemList = regex.Matches(aPattern);

                foreach (Match m in itemList) {
                    switch (m.ToString().ToLower()[0]) {
                         case '%':
                              if (m.Length > 1) {
                                   string stringText = 
                                        m.ToString().Substring(1,m.Length-1);
                                   commandList.Add(new 
                                        ExpressionCodeConstantString(stringText));
                                       }
                                   break;
                         case 'y':
                              commandList.Add(new ExpressionCodeYear());
                              break;
                         case 'm':
                              commandList.Add(new ExpressionCodeMonth());
                              break;
                         case 'd':
                              commandList.Add(new ExpressionCodeDay());
                              break;
                         case 'n':
                              commandList.Add(new ExpressionCodeNamespace());
                              break;
                     }
                }
                return commandList;
        }

Interpreter Pattern – second illustration.

My second illustration of the Interpreter pattern is to write a query command to interrogate a date known to the context. In this example we implement a query language that would allow the user to write a query string that would interrogate a date and return a boolean result. This second example has been included to demonstrate the use of non-terminal nodes in the query expression which allows logical nesting of sub queries within the query expression.

A comment block follows that explains the syntax of the language that we will implement. I won't include the parsing step this time as it is incidental to the Interpreter pattern as proscribed by the GOF, and is complex enough to be a diversion. The source is available in my demonstration program if you are interested.


        /*---------------------------------------------------------------
        Parse a BORCON date query expression string, and build an Interpreter
        pattern command structure as directed. The expression will allow the
        user to write a query which will return a true|false result. In this
        example they are querying the date of the 2004 USA BORCON. The query
        syntax uses the following elements to build sub queries:
                identifier: y  = year
                            m  = month
                            d  = day
                operator  : <,>,=
                value     : digits (year is in form yyyy, eg. 2004, not 04)

        Sub queries may be ANDed or ORed against each other.

        Examples of valid queries are:
                y=2004
                (y=2004)
                (y=2004)&(m=12)
                ((y=2004)&(m=12))|(y<2004) etc  (any depth is allowed)

        The last example above would produce the following Interpreter syntax tree:
                -+->new ConcreteQueryNonTerminalORExpression()
                 +--+--> new ConcreteQueryNonTerminalANDExpression()
                    +-----> new QueryTerminalYear('=',2004)
                    +-----> new QueryTerminalMonth('=',12)
                 +-----> new QueryTerminalYear('<',2004)
      --------------------------------------------------------------------*/

Here is the client call to initiate the demonstration:

        /* This 2nd illustration of the Interpreter pattern allows the
           client to interrogate a date known to the Context instance (it is
           the date of the 2004 USA Borcon). The result of the interpretation
           will be a boolean. The gramme allows the +, > and < operators.
           The year, month and day can be tested. Sub-queries can be ANDed
           or ORed against each other to any level of nesting. The syntax
           tree is built at runtime by parsing a command string. */
        ShowUserCommentary(3);
        string script = ((y=2004)&(d>10))&((m=10)|(m=9));
        QueryExpressionBase query = parse.ParseQuery(script, testOutput);
        Context_Query queryContext = new Context_Query();
        query.Interpret(queryContext);

        listBox1.AppendText(String.Format(nnThe query evaluates as {0},
                queryContext.value.ToString()));

To implement this illustration of the Interpreter pattern we firstly we need to define the Context:

    // --- Context
    public class Context_Query {
        // fields
        internal bool _value;
        internal readonly DateTime _Borcon2004Start = new DateTime(2004,9,11);
        // properties
        public bool value {
            get { return _value; }
        }
    }

Then there is the base class that is shared by both terminal and non-terminal Expression nodes:

       public abstract class QueryExpressionBase {
                // methods
                public abstract void Interpret(Context_Query aContext);
        }

Now the implementation of the syntax tree. This time the tree is composed from terminal and non-terminal nodes. In the terminal nodes we evaluate a sub-query which have uses a =, < or > operator. In the non-terminal node we and or or the results of two sub-queries.

                ((y=2004)&(m=12))|(y<2004) etc  (any depth is allowed)

                  (y=2004) and (m=12) are examples of sub queries.

Firstly let's look at the terminal nodes. The following shows their abstract ancestor, and the implementation of the concrete class which handles the year comparisons. There are similar class which handle month and day comparisons which have been omitted for brevity.

       public abstract class QueryTerminalBase: QueryExpressionBase {
                // fields
                protected char _operation;
                protected int _ComparisonValue1, _ComparisonValue2;
                // methods
                override public  void Interpret(Context_Query aContext) {
                        switch (_operation) {
                                case '=':
                                        aContext._value = 
                                           (_ComparisonValue1 == _ComparisonValue2);
                                        break;
                                case '>':
                                        aContext._value = 
                                           (_ComparisonValue1 > _ComparisonValue2);
                                        break;
                                case '<':
                                        aContext._value = 
                                           (_ComparisonValue1 < _ComparisonValue2);
                                        break;
                                default:
                                        throw new ApplicationException(String.Format(Unexpected operation; operation was {0}. Only =, < or > allowed.,
                                            _operation));
                     }
              }
       }

       public class QueryTerminalYear: QueryTerminalBase {
                // constructors
              public QueryTerminalYear(char aOperation, int aValue) {
                     _operation = aOperation;
                        _ComparisonValue2 = aValue;
              }
              // methods
                override public void Interpret(Context_Query aContext) {
                     _ComparisonValue1 = aContext._Borcon2004Start.Year;
                        base.Interpret(aContext);
                }
       }

Finally we need to look at the non-terminal classes which handle the and and or operations. Recursion handles the situation where were there are nested sub-queries.

       abstract public class QueryNonTerminalExpressionBase: QueryExpressionBase {
                // fields
                private ArrayList _childContents = new ArrayList();
                protected bool _leftSide = false, _rightSide = false;
                protected bool _firstSide = true;
                // methods
                override public  void Interpret(Context_Query aContext) {
                        _firstSide = true;
                        foreach (QueryExpressionBase xx in _childContents) {
                                xx.Interpret(aContext);
                                if (_firstSide)
                                        _leftSide = aContext._value;
                                else
                                        _rightSide = aContext._value;
                                        _firstSide = false;
                        }
                }

                public void Add(QueryExpressionBase aExpression) {
                        _childContents.Add(aExpression);
                }
        }

       public class ConcreteQueryNonTerminalANDExpression: 
                                 QueryNonTerminalExpressionBase {
                // methods
                override public  void Interpret(Context_Query aContext) {
                        base.Interpret(aContext);
                    aContext._value = _leftSide & _rightSide;
                }
        }

       public class ConcreteQueryNonTerminalORExpression:
                                 QueryNonTerminalExpressionBase {
                // methods
                override public  void Interpret(Context_Query aContext) {
                        base.Interpret(aContext);
                        aContext._value = _leftSide | _rightSide;
                }
        }

As noted above, I have omitted the parsing step from this article as it is not really part of the Interpreter pattern. The source code is available in the demonstration program

Conclusion

This article has begun examination of the GOF's Behavioural Patterns from those described in their book titled Design patterns. I think that the study of the design patterns is a worthwhile thing to do. I found the following links useful while studying the patterns myself and while preparing this article. The book itself is a good investment as it provides supplementary detail upon the problems that the patterns are trying to solve, the elements of the solution, and the consequences and trade-off's involved in using the patterns.

Look out for my closing article in this series will will study the remaining Behavioural Patterns.

Links

  • http://www.ultrapico.com... Expresso – an excellent free ware tool for building and testing Regular Expressions (as used within the .Net Regex class).


Server Response from: SC3