Visitor Pattern
From eqqon
| m (→The complete example Program) |  (→Motivation for applying the Visitor Pattern) | ||
| Line 2: | Line 2: | ||
| == Motivation for applying the Visitor Pattern == | == Motivation for applying the Visitor Pattern == | ||
| + | Imagine you have a big class hierarchy and you are writing a sophisticated class that does something different for each type in the hierarchy. You cannot put the functionality into the respective classes for some reason. Here is a very simplified code example which demonstrates the situation and implements a naive solution. | ||
| + | |||
| + | === Example === | ||
| + | <span><span class="S5">public</span><span class="S0"> </span><span class="S5">class</span><span class="S0"> </span>A<span class="S0"> </span><span class="S10">{}</span><br /> | ||
| + | <span class="S5">public</span><span class="S0"> </span><span class="S5">class</span><span class="S0"> </span>B<span class="S0"> </span><span class="S10">:</span><span class="S0"> </span>A<span class="S0"> </span><span class="S10">{}</span><br /> | ||
| + | <span class="S5">public</span><span class="S0"> </span><span class="S5">class</span><span class="S0"> </span>C<span class="S0"> </span><span class="S10">:</span><span class="S0"> </span>B<span class="S0"> </span><span class="S10">{}</span><br /> | ||
| + | <br /> | ||
| + | <span class="S5">public</span><span class="S0"> </span><span class="S5">class</span><span class="S0"> </span>Printer<br /> | ||
| + | <span class="S10">{</span><br /> | ||
| + | <span class="S0">    </span><span class="S5">public</span><span class="S0"> </span><span class="S5">void</span><span class="S0"> </span>Print<span class="S10">(</span>A<span class="S0"> </span>element<span class="S10">)</span><span class="S0"> </span><span class="S10">{</span><span class="S0"> </span>Console<span class="S10">.</span>WriteLine<span class="S10">(</span><span class="S6">"A"</span><span class="S10">);</span><span class="S0"> </span><span class="S10">}</span><br /> | ||
| + | <span class="S0">    </span><span class="S5">public</span><span class="S0"> </span><span class="S5">void</span><span class="S0"> </span>Print<span class="S10">(</span>B<span class="S0"> </span>element<span class="S10">)</span><span class="S0"> </span><span class="S10">{</span><span class="S0"> </span>Console<span class="S10">.</span>WriteLine<span class="S10">(</span><span class="S6">"B"</span><span class="S10">);</span><span class="S0"> </span><span class="S10">}</span><br /> | ||
| + | <span class="S0">    </span><span class="S5">public</span><span class="S0"> </span><span class="S5">void</span><span class="S0"> </span>Print<span class="S10">(</span>C<span class="S0"> </span>element<span class="S10">)</span><span class="S0"> </span><span class="S10">{</span><span class="S0"> </span>Console<span class="S10">.</span>WriteLine<span class="S10">(</span><span class="S6">"C"</span><span class="S10">);</span><span class="S0"> </span><span class="S10">}</span><br /> | ||
| + | <span class="S0">    </span><span class="S5">public</span><span class="S0"> </span><span class="S5">void</span><span class="S0"> </span>Print<span class="S10">(</span>A<span class="S10">[]</span><span class="S0"> </span>array<span class="S10">)</span><br /> | ||
| + | <span class="S0">    </span><span class="S10">{</span><br /> | ||
| + | <span class="S0">        </span><span class="S5">foreach</span><span class="S0"> </span><span class="S10">(</span>A<span class="S0"> </span>element<span class="S0"> </span><span class="S5">in</span><span class="S0"> </span>array<span class="S10">)</span><br /> | ||
| + | <span class="S0">        </span><span class="S10">{</span><br /> | ||
| + | <span class="S0">            </span><span class="S2">// here we reimplement polymorphic method dispatching!</span><br /> | ||
| + | <span class="S0">            </span><span class="S5">if</span><span class="S0"> </span><span class="S10">(</span>element<span class="S0"> </span><span class="S5">is</span><span class="S0"> </span>C<span class="S10">)</span><br /> | ||
| + | <span class="S0">                </span>Print<span class="S10">(</span>element<span class="S0"> </span><span class="S5">as</span><span class="S0"> </span>C<span class="S10">);</span><br /> | ||
| + | <span class="S0">            </span><span class="S5">else</span><span class="S0"> </span><span class="S5">if</span><span class="S0"> </span><span class="S10">(</span>element<span class="S0"> </span><span class="S5">is</span><span class="S0"> </span>B<span class="S10">)</span><br /> | ||
| + | <span class="S0">                </span>Print<span class="S10">(</span>element<span class="S0"> </span><span class="S5">as</span><span class="S0"> </span>B<span class="S10">);</span><br /> | ||
| + | <span class="S0">            </span><span class="S5">else</span><span class="S0"> </span><span class="S5">if</span><span class="S0"> </span><span class="S10">(</span>element<span class="S0"> </span><span class="S5">is</span><span class="S0"> </span>A<span class="S10">)</span><br /> | ||
| + | <span class="S0">                </span>Print<span class="S10">(</span>element<span class="S10">);</span><br /> | ||
| + | <span class="S0">        </span><span class="S10">}</span><br /> | ||
| + | <span class="S0">    </span><span class="S10">}</span><br /> | ||
| + | <span class="S10">}</span><br /> | ||
| + | <span class="S0"></span></span> | ||
| + | |||
| + | As you can imagine this naive implementation would become very complicated for a larger and deeper class hierarchy. It would be error prone and unmaintainable. Every time you add a new class to your hierarchy you would need to extend the dispatching logic in the Printer class. | ||
| + | |||
| + | == Applying the Visitor Pattern == | ||
| == The complete example program == | == The complete example program == | ||
Revision as of 09:23, 21 November 2007
| Contents | 
Motivation for applying the Visitor Pattern
Imagine you have a big class hierarchy and you are writing a sophisticated class that does something different for each type in the hierarchy. You cannot put the functionality into the respective classes for some reason. Here is a very simplified code example which demonstrates the situation and implements a naive solution.
Example
public class A {}
public class B : A {}
public class C : B {}
public class Printer
{
    public void Print(A element) { Console.WriteLine("A"); }
    public void Print(B element) { Console.WriteLine("B"); }
    public void Print(C element) { Console.WriteLine("C"); }
    public void Print(A[] array)
    {
        foreach (A element in array)
        {
            // here we reimplement polymorphic method dispatching!
            if (element is C)
                Print(element as C);
            else if (element is B)
                Print(element as B);
            else if (element is A)
                Print(element);
        }
    }
}
As you can imagine this naive implementation would become very complicated for a larger and deeper class hierarchy. It would be error prone and unmaintainable. Every time you add a new class to your hierarchy you would need to extend the dispatching logic in the Printer class.
Applying the Visitor Pattern
The complete example program
using System;
using System.Collections.Generic;
using System.Text;
namespace CodeSnippet
{
    public class A
    {
        public virtual void Print(VisitorPattern.Printer printer) { printer.Print(this); } // calls Print(A element)
    }
    public class B : A
    {
        public override void Print(VisitorPattern.Printer printer) { printer.Print(this); } // calls Print(B element)
    }
    public class C : B
    {
        public override void Print(VisitorPattern.Printer printer) { printer.Print(this); } // calls Print(C element)
    }
    namespace NaiveApproach
    {
        public class Printer
        {
            public void Print(A element) { Console.WriteLine("A"); }
            public void Print(B element) { Console.WriteLine("B"); }
            public void Print(C element) { Console.WriteLine("C"); }
            public void Print(A[] array)
            {
                foreach (A element in array)
                {
                    // here we reimplement polymorphic method dispatching!
                    if (element is C)
                        Print(element as C);
                    else if (element is B)
                        Print(element as B);
                    else if (element is A)
                        Print(element);
                }
            }
        }
    }
    namespace VisitorPattern
    {
        public class Printer
        {
            public void Print(A element) { Console.WriteLine("A"); }
            public void Print(B element) { Console.WriteLine("B"); }
            public void Print(C element) { Console.WriteLine("C"); }
            public void Print(A[] array)
            {
                foreach (A element in array)
                {
                    // here we make use of the language's polymorphic method dispatching
                    element.Print(this);
                }
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A[] array = { new A(), new B(), new C() };
            Console.WriteLine("Naive Approach:");
            new NaiveApproach.Printer().Print(array);
            Console.WriteLine("Visitor Pattern:");
            new VisitorPattern.Printer().Print(array);
            Console.ReadLine();
        }
    }
}