Visitor Pattern

From eqqon

(Difference between revisions)
Jump to: navigation, search
m (Example)
m
 
(7 intermediate revisions not shown)
Line 1: Line 1:
[[Category:CSharp]]
[[Category:CSharp]]
-
 
+
__NOTOC__
-
== Motivation for applying the Visitor Pattern ==
+
<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.
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 ===
+
== 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><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>B<span class="S0"> </span><span class="S10">:</span><span class="S0"> </span>A<span class="S0"> </span><span class="S10">{}</span><br />
Line 29: Line 30:
<span class="S10">}</span><br />
<span class="S10">}</span><br />
<span class="S0"></span></span>
<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.
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.
-
== Applying the Visitor Pattern ==
+
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 119: 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)