Visitor Pattern

From eqqon

(Difference between revisions)
Jump to: navigation, search
m (The complete example Program)
m
 
(9 intermediate revisions not shown)
Line 1: Line 1:
[[Category:CSharp]]
[[Category:CSharp]]
 +
__NOTOC__
 +
<div style = "margin-left:100px; margin-right:200px;">
 +
= Motivation for applying the Visitor Pattern (C#)=
 +
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.
-
== Motivation for applying the Visitor Pattern ==
+
== 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>
 +
<div>
 +
 
 +
== Applying the Visitor Pattern ==
 +
As you can imagine this naive implementation would become very complicated for a larger and deeper class hierarchies. 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.
 +
 
 +
The '''Visitor Pattern [Gamma et al. 95]''' offers a better solution. It makes use of the polymorphic method dispatching functionality of object oriented programming languages (also called '''late binding''') effectively eliminating the error prone part of our naive implementation. The idea is to '''double dispatch''' the method call by asking each type to re-dispatch the call to Print accordingly. The drawback is that we have to extend our class hierarchy with boilerplate dispatching methods like this:
 +
 
 +
<span><span class="S0"></span><br />
 +
<span class="S0">&nbsp; &nbsp; </span><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<br />
 +
<span class="S0">&nbsp; &nbsp; </span><span class="S10">{</span><br />
 +
<span class="S0">&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="S5">public</span><span class="S0"> </span><span class="S5">override</span><span class="S0"> </span><span class="S5">void</span><span class="S0"> </span>Print<span class="S10">(</span>VisitorPattern<span class="S10">.</span>Printer<span class="S0"> </span>printer<span class="S10">)</span><span class="S0"> </span><span class="S10">{</span><span class="S0"> </span>printer<span class="S10">.</span>Print<span class="S10">(</span><span class="S5">this</span><span class="S10">);</span><span class="S0"> </span><span class="S10">}</span><span class="S0"> </span><span class="S2">// calls Print(B element)</span><br />
 +
<span class="S0">&nbsp; &nbsp; </span><span class="S10">}</span><br />
 +
<span class="S0"></span></span>
 +
 
 +
These, however, are not complicated and thus less prone to errors. Of course the Visitor Pattern is prone to another type of error, namely, forgetting the dispatching method for some subtype in the hierarchy. See the following example program for the implementation of the Visitor Pattern.
== The complete example program ==
== The complete example program ==
Line 88: Line 131:
<span class="S10">}</span><br />
<span class="S10">}</span><br />
<span class="S0"></span></span>
<span class="S0"></span></span>
 +
 +
--[[User:Henon|Henon]] 11:16, 21 November 2007 (CET)

Latest revision as of 22:52, 21 November 2007


Motivation for applying the Visitor Pattern (C#)

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);
        }
    }
}

Applying the Visitor Pattern

As you can imagine this naive implementation would become very complicated for a larger and deeper class hierarchies. 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.

The Visitor Pattern [Gamma et al. 95] offers a better solution. It makes use of the polymorphic method dispatching functionality of object oriented programming languages (also called late binding) effectively eliminating the error prone part of our naive implementation. The idea is to double dispatch the method call by asking each type to re-dispatch the call to Print accordingly. The drawback is that we have to extend our class hierarchy with boilerplate dispatching methods like this:


    public class B : A
    {
        public override void Print(VisitorPattern.Printer printer) { printer.Print(this); } // calls Print(B element)
    }

These, however, are not complicated and thus less prone to errors. Of course the Visitor Pattern is prone to another type of error, namely, forgetting the dispatching method for some subtype in the hierarchy. See the following example program for the implementation of 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();
        }
    }
}

--Henon 11:16, 21 November 2007 (CET)