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