I've become a fan of the visitor design pattern recently (a side effect of researching how lambda expression parsing works in C#), and have started using it frequently in my own code.
Something I've noticed, however, is that when this pattern is discussed in articles/tutorials, the use cases are almost always trees/graphs.
Why is this the case? Isn't the pattern useful for non-graph data structures as well, such as lists and arrays? Is there something about the visitor pattern that makes it especially suitable for trees, as opposed to other structures?
CodePudding user response:
As wiki says about visitor pattern:
A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle
Yeah, you can use this pattern for lists and arrays. In my view, tutorials use graph and tree structure because they want to highlight that new behaviour can be added without editing code in existing classes stored in not simple data structure.
Let me show an example with array via C#.
Abstractions:
public interface IBicyclePart
{
void Accept(IBicyclePartVisitor bicyclePartVisitor);
}
public interface IBicyclePartVisitor
{
void Visit(Bicycle bicycle);
void Visit(Treadle treadle);
void Visit(Wheel wheel);
void Visit(Seat Seat);
}
Concrete classes:
public class Bicycle : IBicyclePart
{
private IBicyclePart[] _parts;
public Bicycle()
{
_parts = new IBicyclePart[] { new Seat(), new Treadle(), new Wheel() };
}
public void Accept(IBicyclePartVisitor computerPartVisitor)
{
for (int i = 0; i < _parts.Length; i )
{
_parts[i].Accept(computerPartVisitor);
}
computerPartVisitor.Visit(this);
}
}
public class Seat : IBicyclePart
{
public void Accept(IBicyclePartVisitor bicyclePartVisitor)
{
bicyclePartVisitor.Visit(this);
}
}
public class Wheel : IBicyclePart
{
public void Accept(IBicyclePartVisitor bicyclePartVisitor)
{
bicyclePartVisitor.Visit(this);
}
}
public class Treadle : IBicyclePart
{
public void Accept(IBicyclePartVisitor bicyclePartVisitor)
{
bicyclePartVisitor.Visit(this);
}
}
And "Run Operation" for bicycle:
public class BicyclePartRunOperationVisitor : IBicyclePartVisitor
{
public void Visit(Bicycle bicycle)
{
Console.WriteLine("Bicycle is running");
}
public void Visit(Wheel wheel)
{
Console.WriteLine("Bike rider manages the wheel");
}
public void Visit(Treadle treadle)
{
Console.WriteLine("Bike rider rotates treadles");
}
public void Visit(Seat seat)
{
Console.WriteLine("Bike rider is at the seat");
}
}
and we can stop our bicycle by adding new operation "Stop Operation":
public class BicyclePartStopOperationVisitor : IBicyclePartVisitor
{
public void Visit(Bicycle bicycle)
{
Console.WriteLine("Bicycle is running");
}
public void Visit(Wheel wheel)
{
Console.WriteLine("Bike rider manages the wheel");
}
public void Visit(Treadle treadle)
{
Console.WriteLine("Bike rider stops rotating treadles");
}
public void Visit(Seat seat)
{
Console.WriteLine("Bike rider is at the seat");
}
}
and then you can call it like this:
static void Main(string[] args)
{
IBicyclePart computer = new Bicycle();
computer.Accept(new BicyclePartRunOperationVisitor());
// computer.Accept(new BicyclePartStopOperationVisitor()); // we are
// using new operation without editing existing classes
}