Visitor
Allows you to walk a an object tree, and perform operations.
Understand the rational behind the pattern with this…
Motivation
Bad implementation

In the image above we have an abstract syntax tree. Each node has 3 methods; these methods are very different like pretty print next to type check. You may walk this tree, and call these methods, but you will have to re-implement duplicate code and or you will at least have unrelated code stat next to each other.
// pretend we are traversing the tree
node.TypeCheck()Good implementation
The Visitor pattern resolves this issue by packaging all those related operations into a single object, the Visitor. You then pass the visitor to each node, the node will accept the Visitor and pass a reference of itself to the Visitor. The Visitor can do then as it pleases.
So this time when you traverse the Tree:
// pretend we are traversing the tree
node.Accept(typeCheckingVisitor)
// then inside node.Accept, depending on the type of node you call a method
// this allows the visitor to know what its visiting, without a switch
visitor.VisitAssignment(this)
// or
visitor.VisitVariableReference(this)
When to use it
- You have an object structure of concrete classes; and the operation depends on the type of concrete class it is
- You have many unrelated operations, and you do not want to put them into the same class
- You are essentially betting that the Nodes will not change, but the operations will regularly
Traversal
The pattern technically doesn't care who does the traversing, only that node calls visitor.
Options:
- Visitor can recursively select child nodes
- An external process
Minimal Code Example
public interface INodeVisitor
{
public void VisitA(ANode node);
public void VisitB(BNode node);
}
public abstract class Node
{
public abstract void Accept(INodeVisitor visitor);
}
public sealed class ANode : Node
{
public override void Accept(INodeVisitor visitor) => visitor.VisitA(this);
}
public sealed class BNode : Node
{
public override void Accept(INodeVisitor visitor) => visitor.VisitB(this);
}