Search  
Friday, September 03, 2010 ..:: Articles » GOF Behavioural Patterns with C# ::..   Login
 Behavioral patterns
Introduction | Chain of Responsibility | Command | Interpreter | Iterator | Mediator | Observer | Momento | State | Strategy | Template | Visitor | Links
 
Show as single page

Visitor Pattern

In this pattern we make a class independent of the operations that can be performed upon it. The operations are then defined within their own separate Visitor classes. The class, or classes, upon which they can operate (Elements) are made "visitor ready" so that they can accept the Visitors.

The Visitor pattern can provide the following benefits:

  • a new operation can be provided without having to change all of the affected Element classes. All we need to do is create a new Visitor class.

  • all of the program logic for a specific operation is gathered together into it's own Visitor class, avoiding a situation where the operation logic is spread across each of the individual Element classes that the operation can affect.

The pattern is especially applicable in the following situations:

  • where the Element classes are beyond the control of the Programmer who will be tasked with adding, or changing, operations that will act upon the Element classes.

  • where the application will have multiple Element classes with differing interfaces, and there is the need to perform the same operations upon all of the Element objects, where the operation algorithms need to be different due to the differences in Element classes being operated upon.

  • where we can achieve code sharing or improved organisation of our program logic, by gathering together the code required for an operation that will be performed upon a diverse set of Element classes.

  • where we expect that we will be more likely to add further, or change existing, operations, than we will be to add further Element classes to be operated upon.

A Visitor may operate upon a individual Element objects, or it may operate upon a Composite collection of Element objects. The Visitor may accumulate information, pertaining to the collection as a whole, as it iterates through all of the member Elements. This is similar in function to an Internal Iterator (see Iterator pattern in the links below), however the Visitor pattern doesn't require that each of the Element objects that it will navigate through share a common base class or interface.

The Visitor pattern has a couple of potential problems:

  • we may be forced to compromise encapsulation, by needing to expose too much of the Element class's internals to make the Element classes "visitor ready"

  • it requires more source code changes if we need to add a new Element class where there are a lot of Visitor classes that will be required to operate upon it.

  • if overused it can increase undesirable coupling within our application, versus a solution where we just used polymorphism; ie. the Elements descend from an Abstract base class, and each Element overriding the operation" methods.

I have two illustrations of the Visitor pattern:

  • the first example has two Element classes, and two Visitor operations to act upon those Elements.

  • the second example shows a Visitor acting upon a composite object containing a mixed collection of Element objects. It operates upon the composite, and then provides statistics about the whole collection.

Here is the client code to run the first illustration:

   /* Construct two Element objects. They inherit from the
    * same base class, but are of different types. Their
    * shared base class is "visitor ready", and has a readonly
    * Value property. Set their Value property by their
    * differing mechanisms to the values "Microsoft" and
    * "Hejlsberg". */
   ElementBase element = new ConcreteElement_1("Microsoft");
   ElementBase element2 = new ConcreteElement_2();
   (ConcreteElement_2)element2).Name = "Hejlsberg";

   ShowUserCommentary(1);
   ShowContentsOfValueProperty(element.Value);  
   ShowContentsOfValueProperty(element2.Value);

   /* Create 2 Visitor objects. One can cause the target
    * Element's Value property contents to be converted to
    * upper case, and the other can cause the characters in
    * the properties contents to become separated by the "_"
    * character. */
   VisitorBase vCapitalise = new ConcreteVistor_1();
   VisitorBase vAddDashes = new ConcreteVistor_2();

   /* Apply the one Visitor to the 1st Element, and both
    * Visitors the 2nd Element. */
   element.Accept(vAddDashes);
   element2.Accept(vCapitalise);
   element2.Accept(vAddDashes);

   ShowUserCommentary(2);
   ShowContentsOfValueProperty(element.Value);
   ShowContentsOfValueProperty(element2.Value);

And this is the output:






The essential interface for the visitor pattern can be thought as as:

       // --- interfaces
        public interface IElementBase {
              void Accept(IVisitorbase v)
        }
        
        public interface IVisitorBase {
        }

The Visitor is passed into the Element's Accept method. The concrete Element class that receives the Accept method call will pass itself into the method within the Visitor object that specifically handles this flavour of Element concrete class as we are about to see. Here is the definition of the base class for the Element classes. The property is just something I need for the demonstration. The key thing is the Accept method, which is what is making the Element classes "visitor ready".

    // Element; objects that get acted upon by the Visitors
    public abstract class ElementBase {
         // fields
         protected string _value;
         // properties
         public string Value {
             get {return _value;}
         }
         // methods
         // ... method to make the Element visitor-ready
         abstract public void Accept(VisitorBase visitor);
    }

Next we shall look at the concrete Element implementations. The key part to focus upon is the overridden Accept methods. The incoming Visitor will have a separate method for each concrete Element class. The overridden Accept method calls the Visitor method that specifically handles this operation for this specific concrete Element class.

The other source code is just something to make the point that each of the concrete classes can have differing interfaces and mechanisms.

   public class ConcreteElement_1: ElementBase {
       // fields
       protected string _text;
       // properties
       public string Text {
           get {return _text;}
           set {
               _text = value;
               _value = value;
           }
       }
       // constructor
       public ConcreteElement_1 (string aText) {
           this.Text = aText;
       }
       // methods
       override public void Accept(VisitorBase visitor) {
           visitor.VisitElement_1(this);
       }
   }

   public class ConcreteElement_2: ElementBase {
       // fields
       private string _name;
       // properties
       public string Name {
           get {return _name;}
           set {
               _name = value;
               _value = value;
           }
       } 
       // methods
       override public void Accept(VisitorBase visitor) {
            visitor.VisitElement_2(this);
       }
   }

Next the definition of a base class for the Visitor classes. We have abstract method definitions for each concrete Element class so the compiler can check for us that each Visitor has been configured to handle all of the concrete Element classes.

// --- Visitors; objects that will act upon upon the Elements
public abstract class VisitorBase {
    // methods
    public abstract void VisitElement_1(ConcreteElement_1 element);
    public abstract void VisitElement_2(ConcreteElement_2 element);
}

Finally we have the Concrete Visitors which do all of the work. As we can see the pattern relies up the fact that the Element classes have exposed enough of their own state to allow the Visitor to achieve it's objective. This exposure requirement can mean that the visitor pattern is unsuitable for our application. This could occur if we required so much exposure of the Element's internal state to the point where we felt that the Element class's encapsulation had been compromised.

   public class ConcreteVistor_1 : VisitorBase {
       // methods
       override public void VisitElement_1(ConcreteElement_1 element) {
           element.Text = element.Text.ToUpper();
       }
       override public void VisitElement_2(ConcreteElement_2 element) {
           element.Name = element.Name.ToUpper();
       }
   }

   public class ConcreteVistor_2 : VisitorBase {
        // methods
        override public void VisitElement_1(ConcreteElement_1 element) {
            element.Text = FormatText(element.Text);

        }
        override public void VisitElement_2(ConcreteElement_2 element) { 
            element.Name = FormatText(element.Name);
        }

        private string FormatText(string aText) {
            StringBuilder retval = new StringBuilder();
            char[] arr;
            arr = aText.ToCharArray();
            foreach (char ch in arr) {
                retval.Append(ch);
                retval.Append('_');
            }
            if (retval.Length > 0) 
                retval.Remove(retval.Length-1,1);
            return retval.ToString();
            }
   }

Visitor pattern – second illustration

In the first illustration we looked at a Visitor being applied to an individual object, this time we will look at the Visitor being dappled to a composite collection of diverse Element types. When our Visitor is acting upon a collection we have the option of accumulating state within the Visitor while it navigates through all the Elements in the collection as we will see in this example.

The collection itself is an implementation of the Composite pattern. This pattern is covered in my article upon the GOF's "Structural Patterns"; see link at the bottom of this article.

In my illustration the concrete Elements classes are various types of fruits. The collection is a fruit bowl that can contain a number of pieces of various types of fruit. Each piece of fruit has a designated weight. We have a "juice" operation that will convert the bowl of fruit into fruit juice. Each fruit type has an assumed percentage juice yield based upon the amount of juice to be expected from a piece of fruit of that type. The weight of juice that is expected from the fruit bowl is therefore determined by the fruit types, and the individual weights of the pieces of fruit.

Here is the snippet of client code, and the private method that it uses to display the results:

     /* Try a different implementation. This time we have a
      * Composite class to contain a collection of Element 
      * classes. The Elements are fruit of various types 
      * and weights. */
     FruitCollection fruitBowl = new FruitCollection();
     fruitBowl.Add(new Apple(1.2f));
     fruitBowl.Add(new Orange(1.4f));
     fruitBowl.Add(new Apple(1.0f));

     /* Create a Visitor. It converts the fruit to juice, and
      * calculates the accumulated juice yield (weight) for 
      * fruit objects that it operates upon. Differing fruit 
      * types may have differing yield percentages. The point 
      * is to show a visitor acting upon a collection, and
      * accumulating state during it's journey. */
     ShowUserCommentary(3);
     Juice juice = new Juice();

     /* Apply the visitor to the collection; convert the fruit
      *  bowl into juice. */.
     fruitBowl.Accept(juice);

     ShowJuiceStats(juice);
     /* Add another Element to the collection, reapply the
      * Visitor to get a different result. */
     ShowUserCommentary(4);
     fruitBowl.Add(new Orange(0.8f));
     juice.Reset();
     fruitBowl.Accept(juice);

     ShowJuiceStatistics(juice);
}

private void ShowJuiceStats(Juice juiceVisitor {    listBox1.AppendText(String.Format(
                     "\n Juice yield weight is {0:0.#}",
                     juiceVisitor.YieldWeight));

     listBox1.AppendText(String.Format(
                     "\n Percentage apple juice = {0:0.#}",
                     juiceVisitor.PercentageApple));
}

Here is the output:






Here is the implementation of the base class for the fruit Elements. The pattern doesn't require that our Element classes share a common base class, although I have one in this illustration. The pattern only requires that the Element classes implement an interface containing the Accept method.

   // --- Base class for the "Element" class in the pattern
   abstract public class FruitBase {
       // fields
       private float _weight;
       // properties
       public float Weight {
           get {return _weight;}
       }
       // constructor
       public FruitBase(float aWeight) {
           _weight = aWeight;
       }
       // methods
       // ... method to make the Element visitor-ready
       abstract public void Accept(Juice visitor);
       public void ResetWeight() {
           _weight = 0;
       }
   }

Next is the implementation of the Composite class for the bowl of fruit. As you can see it implements it's own "Accept" method, which iterates through the collection's member Elements, and calling the Accept method for each.

   // --- Collection of the "Element" objects
   public class FruitCollection {
       // fields
       private List<FruitBase > _collection = 
              new List<FruitBase >();
       // methods
       public void Add(FruitBase aFruit) {
           _collection.Add(aFruit);
       }   
       public void Remove(FruitBase aFruit) {
           _collection.Remove(aFruit);
       }
       public void Accept(Juice visitor) {
            foreach (FruitBase fruit in _collection) {
                fruit.Accept(visitor);
            }
       }
   }

Next the concrete Element classes:

        // --- Concrete "Element" classes
        public class Apple: FruitBase {
                // constructor
                public Apple(float aWeight) :base(aWeight) {
                }
                // methods
                override public void Accept(Juice visitor) {
                        visitor.VisitApple(this);
                }
        }

        public class Orange: FruitBase {
                // constructor
                public Orange(float aWeight) :base(aWeight) {
                }
                // methods
                override public void Accept(Juice visitor) {
                        visitor.VisitOrange(this);
                }
        }

Finally the implementation of the Visitor class. It contains the "juice" methods for each Element class type, and also the mechanisms for accumulating and returning state values.

   // --- Concrete "Visitor" class
   public class Juice {
       // fields
       protected float _weightApples, _weightOranges;
       // properties
       public float PercentageApple {
           get {return 
               (_weightApples * 100)/(_weightApples 
                         + _weightOranges) ;}
           }    
       public float YieldWeight {
           get {return (_weightApples + _weightOranges) ;}
       }
       // methods
       public void VisitApple(Apple fruit) {
           _weightApples += fruit.Weight * .85f;
       }
       public void VisitOrange(Orange fruit) {
           _weightOranges 
               += fruit.Weight * .75f;  // thick skin, less yield
       }
       public void Reset() {
           _weightApples = 0;
           _weightOranges = 0;
       }
   }

Template | Page 12 of 13 | Links

      

Copyright 2005 by Primos Computer Services   Terms Of Use  Privacy Statement