Visitor Pattern
From eqqon
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();
}
}
}