Code Design
Courses and Workshops on Effective Code Design
Post-image

The Visitor Pattern - A Better Implementation

Even if it has been a while since my last post (I've been very busy lately with giving my training), one piece of feedback that I got back then, stuck into my head:

You can make a better implementation of it. I remember somewhere a better implementation of the Visitor Pattern in C#.

So, last week, having some dead time while flying, I've pulled out my laptop and tried to improve the same sample code I did in the previous post.

In that post we have taken as example a CommandsMananger which holds a structure of commands, and it has to do different operations on these commands, like PrettyPrint() and Approve().

The implementation we have ended up with has a ReportVisitor which builds a report while each command is visited, and at the end it prints the report.

class ReportVisitor : IVisitor
{
	private StringBuilder report = new StringBuilder();

	public void VisitCustomerCommand(CustomerCommand customerCommand)
	{
		report.AppendLine($"VisitCustomerCommand customer command: {customerCommand.Name} in business: {customerCommand.BusinessDomain}");
	}

	public void VisitSalesOrderCommand(SalesOrderCommand salesOrderCommand)
	{
		report.AppendLine("Sales order command: ");
		foreach (var line in salesOrderCommand.OrderLines)
		{
			report.AppendLine($"\t Product={line.Product} Quantity={line.Quantity}");
		}
	}

	public void VisitPurchaseOrderCommand(PurchaseOrderCommand purchaseOrder)
	{
		report.AppendLine($"Purchase order command: Product={purchaseOrder.Product} Quatity={purchaseOrder.Quantity}");
	}

	public void Print()
	{
		Console.WriteLine(report);
	}
}

This is a visitor which holds state (the report), and when each command is visited the state accumulates.

For the Approve() operation we ended up with more visitors. One for each type o the commands. For example the visitor for the PurchaseOrderCommand looked like this:

class PurchaseOrderCommandApprover : IVisitor
{
	public void VisitPurchaseOrderCommand(PurchaseOrderCommand purchaseOrder)
	{
		// code that approves the command of creating a new purchase order.
		// this code may use external classes or services to the approval
	}
	public void VisitCustomerCommand(CustomerCommand customerCommand)
	{	// we do nothing here because we only deal with new purchase orders approval
	}

	public void VisitSalesOrderCommand(SalesOrderCommand salesOrderCommand)
	{	// we do nothing here because we only deal with new purchase orders approval
	}
}

These are visitors without state. When a command is visited it gets approved or not.

The thing which is not nice about this implementation is that we have to have functions for all the command types even if we don't want to do anything with some commands when they are visited. Above, the PurchaseOrderCommandApprover class is only interested on the PurchaseOrderCommand and it will have no code on the functions VisitCustomerCommand() or VisitCustomerCommand().

The most common implementation that I've seen to get rid of the unwanted functions is to have an abstract base class (like in the Template Method pattern) for the visitor and then we just override the methods that we want. In our example it would be like this (here in v4 is the full code):

abstract class Visitor : IVisitor
{
    public virtual void VisitCustomerCommand(NewCustomerCommand customerCommand)
    {
    }

    public virtual void VisitSalesOrderCommand(NewSalesOrderCommand salesOrderCommand)
    {
    }

    public virtual void VisitPurchaseOrderCommand(NewPurchaseOrderCommand purchaseOrder)
    {
    }
}

and for a concrete visitor we would have:

class PurchaseOrderCommandApprover : Visitor
{
	public override void VisitPurchaseOrderCommand(NewPurchaseOrderCommand purchaseOrder)
	{
		// code that approves the command of creating a new purchase order.
		// this code may use external classes or services to the approval
	}
}

Nice! We have small classes for the specific visitors.

This is a simple and a good solution for most of the cases. Especially for those where the types of the items visited do not change. This is the one chosen for the Lambda Expression Tree Visitor in .NET. There, you will inherit from the ExpressionVisitor class and override the function that interests you.

Even if this is simple, it doesn't mean it is the nicest solution. I don't like it the most for two reasons:

  1. it uses inheritance, and I have a heavy bias against inheritance
  2. in the case that a new type of command needs to be added we will have to add new functions to the IVisitor interface and to the Visitor class.

So for the sake of exercise, lets go forward and see if we can make it better.

There are two directions from where we can get to a better implementation. Both have to do with generics (here in v5 is the full code for this version).

One direction is to start from the visitors implementations. Another way to have only the functions that we need in a visitor class is to make it implement a generic interface:

public interface IVisitor<TElement>
{
    void Visit(TElement element);
}

With this, the visitors get only the functions they want. Even more the resulted objects will have only those functions. The PurchaseOrderCommandApprover is as simple as above:

class PurchaseOrderCommandApprover : IVisitor<NewPurchaseOrderCommand>
{
	public void VisitPurchaseOrderCommand(NewPurchaseOrderCommand purchaseOrder)
	{
		// code that approves the command of creating a new purchase order.
		// this code may use external classes or services to the approval
	}
}

and the Report visitor which is interested in all the commands will implement the generic interfaces for each command type:

class Report : IVisitor<NewCustomerCommand>,
               IVisitor<NewSalesOrderCommand>,
               IVisitor<NewPurchaseOrderCommand>
{
    readonly StringBuilder report = new StringBuilder();

    public void Print()
    {
        Console.WriteLine(report);
    }

    public void Visit(NewCustomerCommand customerCommand)
    {
        report.AppendLine($"New customer request: {customerCommand.Name} in business: {customerCommand.BusinessDomain}");
    }

    public void Visit(NewSalesOrderCommand salesOrderCommand)
    {
        report.AppendLine("Sales order request: ");
        foreach (var line in salesOrderCommand.OrderLines)
        {
            report.AppendLine($"\t - Product={line.Product} Quantity={line.Quantity}");
        }
    }

    public void Visit(NewPurchaseOrderCommand purchaseOrder)
    {
        report.AppendLine($"Purchase order request: Product={purchaseOrder.Product} Quantity={purchaseOrder.Quantity}");
    }
}

The other direction is to start from the IVisitable interface, which on the Accept(IVisitor visitor) function needs a parameter of a non-generic interface. So, the non-generic interface IVisitor is still needed, but it doesn't mean it needs to have one function for each command type. Instead it may have a generic function, like:

public interface IVisitor
{
    void Visit<TElement>(TElement element);
}

Now, if we look again at the interfaces we've obtain we have the non-generic IVisitor above, the generic IVisitor<TElement> below and the IVisitable below:

public interface IVisitor<TElement>
{
    void Visit(TElement element);
}
public interface IVisitable
{
     void Accept(IVisitor visitor);
}

Nice! None of the interfaces have any knowledge of the types of the commands (or more abstract said: the items) which are visited. When a new type of command is needed none of the interfaces, nor its implementation must be changed!

To make all this work one last piece of the puzzle remains: the link between the two IVisitor and IVisitor<TElement> interfaces. This is realized by a general implementation of the IVisitor, which is very simple. It wraps the specific visitor and delegates the visit to it:

sealed class Visitor : IVisitor
{
    private readonly object specificVisitor;

    public Visitor(object specificVisitor)
    {
        this.specificVisitor = specificVisitor;
    }

    public void Visit<TElement>(TElement element)
    {
        IVisitor<TElement> v = visitor as IVisitor<TElement>;
        v?.Visit(element);
    }
}

So, when a IVisitable command is visited, on the Accept() function the above Visitor.Visit<TElement>(TElement element) is called. It casts the specificVisitor to the generic IVisitor<TElement> and if that succeeds (the specific visitor is interested in this element type), it forwards the visit to it.

The client code wires up the visitors, by wrapping the specific visitor into the general one:

public class CommandsManager
{
    private readonly List<IVisitable> items = new List<IVisitable>();

    public void PrettyPrint()
    {
        Report report = new Report(); // the specific visitor
        IVisitor reportVisitor = new Visitor(report); // constructs the general visitor as a wrapper over the specific one.

        foreach (var item in items)
        {
            item.Accept(reportVisitor);
        }

        report.Print();
    }
....
}

And voilĂ ! Everything works nicely! (the full code for this implementation is here in v5, where you can also run the demo and see how everything works together).

I find this implementation to be nicer than the previous ones. It is very flexible, we can easily adapt it to changes and if we'd use a smart Dependency Injection Container the wire-up of the general visitor and the specific visitor could be more elegant.

A small variation of it is to make the link between the two IVisitor interfaces visible in the interfaces themselves. We can do it by adding the AsVisitor() function like this:

public interface IVisitor<TElement>
{
     IVisitor AsVisitor();

     void Visit(TElement element);
}

Another advantage of this variation is that it makes the client code simpler, because it does not need to know nor to wire the two IVisitor interfaces:

public class CommandsManager
{
    private readonly List<IVisitable> items = new List<IVisitable>();

    public CommandsManager()
    {
        this.items.AddRange(DemoData.GetItems());
    }

    public void PrettyPrint()
    {
        ReportVisitor reportVisitor = new ReportVisitor(); //we just construct the visitor

        foreach (var item in items)
        {
            item.Accept(reportVisitor.AsVisitor()); // we pass the general visitor
        }

        reportVisitor.Print();
    }
...
}

On the other hand it makes the visitor implementations a bit more complicated, because now the wiring happens here:

class ReportVisitor :   IVisitor<NewCustomerCommand>,
                        IVisitor<NewSalesOrderCommand>,
                        IVisitor<NewPurchaseOrderCommand>
{
    readonly StringBuilder report = new StringBuilder();
    private readonly IVisitor visitor;

    public ReportVisitor()
    {
        visitor = new Visitor(this);
    }

    public IVisitor AsVisitor()
    {
        return visitor;
    }
...
}

To sum it up, we have started from the "by the book" implementation of the Visitor Pattern that we've done in the previous post, and we've tried to improve it gradually. We have reached to a flexible implementation that uses generics and which does not hard code the types of the items being visited. I think this is one of the greatest differences. This opens the possibility to embed a generic Visitor Pattern implementation somewhere in the infrastructure of a project, but... more about this in a future post.

The entire source code with all the versions is available on Github as part of my Design Patterns Explained course, structured in a folder for each version:

  • v3 - is the state where we left off in the previous post. A "by the book" implementation
  • v4 - uses a base class to let specific visitors override only the methods they are interested in
  • v5 - is a better implementation based on generics
  • v6 - is a small variation of v5, by adding an explicit link between the IVisitor interfaces
Many more patterns are explained with examples in my Design Patterns Explained course
Featured image source: IMDb - Vikings

You've successfully subscribed to Code Design
Great! Next, complete checkout to get full access to all premium content.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
Success! Your billing info is updated.
Error! Billing info update failed.