Visitor Pattern

From eqqon

(Difference between revisions)
Jump to: navigation, search
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">&nbsp; &nbsp; </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">&nbsp; &nbsp; </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">&nbsp; &nbsp; </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">&nbsp; &nbsp; </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">&nbsp; &nbsp; </span><span class="S10">{</span><br />
 +
<span class="S0">&nbsp; &nbsp; &nbsp; &nbsp; </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">&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="S10">{</span><br />
 +
<span class="S0">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="S2">// here we reimplement polymorphic method dispatching!</span><br />
 +
<span class="S0">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </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">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </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">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </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">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </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">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </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">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span>Print<span class="S10">(</span>element<span class="S10">);</span><br />
 +
<span class="S0">&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="S10">}</span><br />
 +
<span class="S0">&nbsp; &nbsp; </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();
        }
    }
}