Visitor Pattern Explanation

Visitor pattern's purpose with examples

Once upon a time...

class MusicLibrary {
private Set<Music> collection ...
public Set<Music> getPopMusic() { ... }
public Set<Music> getRockMusic() { ... }
public Set<Music> getElectronicaMusic() { ... }
}

Then you realize you'd like to be able to filter the library's collection by other genres. You could keep adding new getter methods. Or you could use Visitors.

interface Visitor<T> {
visit(Set<T> items);
}

interface MusicVisitor extends Visitor<Music>;

class MusicLibrary {
private Set<Music> collection ...
public void accept(MusicVisitor visitor) {
visitor.visit( this.collection );
}
}

class RockMusicVisitor implements MusicVisitor {
private final Set<Music> picks = ...
public visit(Set<Music> items) { ... }
public Set<Music> getRockMusic() { return this.picks; }
}
class AmbientMusicVisitor implements MusicVisitor {
private final Set<Music> picks = ...
public visit(Set<Music> items) { ... }
public Set<Music> getAmbientMusic() { return this.picks; }
}

You separate the data from the algorithm. You offload the algorithm to visitor implementations. You add functionality by creating more visitors, instead of constantly modifying (and bloating) the class that holds the data.

Visitor Pattern Explanation

Visitor pattern is used to implement double dispatch. In plain words it means that the code that gets executed depends on runtime types of two objects.

When you call a regular virtual function, it is a single dispatch: the piece of code that gets executed depends on the runtime type of a single object, namely, the one the virtual method of which you are calling.

With the visitor pattern, the method that is being called ultimately depends on the type of two objects - the type of the object implementing the equipmentVisitor, and the type of the object on which you call accept (i.e. the equipmentVisited subclass).

There are other ways to implement double dispatch in C++. Item 31 of Scott Meyer's "More Effective C++" treats this subject in depth.

What is the benefit of Visitor pattern in an API?

The visitor pattern is normally used when you have a polymorphic type and you want to perform an externally defined operation based on the specific subtype of the object. In your example, CoursesResult is a polymorphic type and a visitor lets you convert a Successful response into an OkObjectResult without directly coupling these two types.

Your alternative approach where CoursesResult directly returns an IActionResult is traditional polymorphism, which is simpler but couples domain logic to the MVC layer.

Now, I don't know what your full set of responses looks like, but if you only have one successful response and the rest are errors, then the simplest approach here is to directly return the successful response and throw exceptions for the other cases:

public async Task<CourseList> GetCourses(UserSession userSession) {
return courseList; /* or */ throw new BadRequestException();
}

Then your controller can simply catch exceptions and convert them to the appropriate IActionResult.

Iterator vs Visitor Design Pattern and How

By using the Visitor pattern you decouple the actions performed on some data structure from the specific structure - i.e. you define some algorithm that is not concerned about what data it will be applied on. It was actually originated to solve the problem of modifying the behavior of classes which could not be modified.

The Iterator pattern decouples the exploration of a data structure from the specific structure.

You can perfectly combine both: use an iterator to move over each item of a data structure and pass a visitor to each item so that an external responsible performs some operation of the item.

IEnumerable in .NET implements an Iterator pattern. Say you have an Item class with a Visit method that takes an IVisitor interface, which visitors implement, and calls some method from that interface to invoke the visitor action. Then you would use the iterator to visit each item of a collection:

IEnumerable<Item> itemCollection = new List<Item>(...);
IVisitor visitor = new VisitorImplementation();

foreach (Item item in itemCollection)
item.Visit(visitor);

Regarding your second question, you might find this great MSDN articule on the Visitor pattern and double dispatch useful. It provides a better explanation of the visitor pattern while also being focused on that topic.

What is the point of accept() method in Visitor pattern?

The visitor pattern's visit/accept constructs are a necessary evil due to C-like languages' (C#, Java, etc.) semantics. The goal of the visitor pattern is to use double-dispatch to route your call as you'd expect from reading the code.

Normally when the visitor pattern is used, an object hierarchy is involved where all the nodes are derived from a base Node type, referred to henceforth as Node. Instinctively, we'd write it like this:

Node root = GetTreeRoot();
new MyVisitor().visit(root);

Herein lies the problem. If our MyVisitor class was defined like the following:

class MyVisitor implements IVisitor {
void visit(CarNode node);
void visit(TrainNode node);
void visit(PlaneNode node);
void visit(Node node);
}

If, at runtime, regardless of the actual type that root is, our call would go into the overload visit(Node node). This would be true for all variables declared of type Node. Why is this? Because Java and other C-like languages only consider the static type, or the type that the variable is declared as, of the parameter when deciding which overload to call. Java doesn't take the extra step to ask, for every method call, at runtime, "Okay, what is the dynamic type of root? Oh, I see. It's a TrainNode. Let's see if there's any method in MyVisitor which accepts a parameter of type TrainNode...". The compiler, at compile-time, determines which is the method that will be called. (If Java indeed did inspect the arguments' dynamic types, performance would be pretty terrible.)

Java does give us one tool for taking into account the runtime (i.e. dynamic) type of an object when a method is called -- virtual method dispatch. When we call a virtual method, the call actually goes to a table in memory that consists of function pointers. Each type has a table. If a particular method is overridden by a class, that class' function table entry will contain the address of the overridden function. If the class doesn't override a method, it will contain a pointer to the base class' implementation. This still incurs a performance overhead (each method call will basically be dereferencing two pointers: one pointing to the type's function table, and another of function itself), but it's still faster than having to inspect parameter types.

The goal of the visitor pattern is to accomplish double-dispatch -- not only is the type of the call target considered (MyVisitor, via virtual methods), but also the type of the parameter (what type of Node are we looking at)? The Visitor pattern allows us to do this by the visit/accept combination.

By changing our line to this:

root.accept(new MyVisitor());

We can get what we want: via virtual method dispatch, we enter the correct accept() call as implemented by the subclass -- in our example with TrainElement, we'll enter TrainElement's implementation of accept():

class TrainNode extends Node implements IVisitable {
void accept(IVisitor v) {
v.visit(this);
}
}

What does the compiler know at this point, inside the scope of TrainNode's accept? It knows that the static type of this is a TrainNode. This is an important additional shred of information that the compiler was not aware of in our caller's scope: there, all it knew about root was that it was a Node. Now the compiler knows that this (root) is not just a Node, but it's actually a TrainNode. In consequence, the one line found inside accept(): v.visit(this), means something else entirely. The compiler will now look for an overload of visit() that takes a TrainNode. If it can't find one, it'll then compile the call to an overload that takes a Node. If neither exist, you'll get a compilation error (unless you have an overload that takes object). Execution will thus enter what we had intended all along: MyVisitor's implementation of visit(TrainNode e). No casts were needed, and, most importantly, no reflection was needed. Thus, the overhead of this mechanism is rather low: it only consists of pointer references and nothing else.

You're right in your question -- we can use a cast and get the correct behavior. However, often, we don't even know what type Node is. Take the case of the following hierarchy:

abstract class Node { ... }
abstract class BinaryNode extends Node { Node left, right; }
abstract class AdditionNode extends BinaryNode { }
abstract class MultiplicationNode extends BinaryNode { }
abstract class LiteralNode { int value; }

And we were writing a simple compiler which parses a source file and produces a object hierarchy that conforms to the specification above. If we were writing an interpreter for the hierarchy implemented as a Visitor:

class Interpreter implements IVisitor<int> {
int visit(AdditionNode n) {
int left = n.left.accept(this);
int right = n.right.accept(this);
return left + right;
}
int visit(MultiplicationNode n) {
int left = n.left.accept(this);
int right = n.right.accept(this);
return left * right;
}
int visit(LiteralNode n) {
return n.value;
}
}

Casting wouldn't get us very far, since we don't know the types of left or right in the visit() methods. Our parser would most likely also just return an object of type Node which pointed at the root of the hierarchy as well, so we can't cast that safely either. So our simple interpreter can look like:

Node program = parse(args[0]);
int result = program.accept(new Interpreter());
System.out.println("Output: " + result);

The visitor pattern allows us to do something very powerful: given an object hierarchy, it allows us to create modular operations that operate over the hierarchy without needing requiring to put the code in the hierarchy's class itself. The visitor pattern is used widely, for example, in compiler construction. Given the syntax tree of a particular program, many visitors are written that operate on that tree: type checking, optimizations, machine code emission are all usually implemented as different visitors. In the case of the optimization visitor, it can even output a new syntax tree given the input tree.

It has its drawbacks, of course: if we add a new type into the hierarchy, we need to also add a visit() method for that new type into the IVisitor interface, and create stub (or full) implementations in all of our visitors. We also need to add the accept() method too, for the reasons described above. If performance doesn't mean that much to you, there are solutions for writing visitors without needing the accept(), but they normally involve reflection and thus can incur quite a large overhead.

Visitor Pattern VS Iterator Pattern: visiting across hierarchy class?

First, you should know what these patterns are for.

The Iterator Pattern is used to access an aggregate sequentially without exposing its underlying representation. So you could Hide a List or array or similar aggregates behind an Iterator.

Visitor Pattern is used to perform an action on a structure of elements without changing the implementation of the elements themselves.

So you use the patterns in two different situations and not as alternatives to each other.

In the Visitor Pattern you implement an Interface IAcceptor in each element you want to visit. So the Visitor Pattern doesn't rely on a superclass but on Interfaces

public interface IAcceptor
{
public void Accept(IVisitor visitor);
}

So if you have a List of objects you can iterate over it and visit the objects implementing IAcceptor

public VisitorExample()
{
MyVisitorImplementation visitor = new MyVisitorImplementation();
List<object> objects = GetList();
foreach(IAcceptor item in objects)
item.Accept(visitor);
}

public interface IVisitor
{
public void Visit(MyAcceptorImplementation item);
public void Visit(AnotherAcceptorImplementation item);
}

public class MyAcceptorImplementation : IAcceptor
{
//Some Code ...
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}

To complete the code here is Visitor to write to Console if it visits my or another implementation of an acceptor.

public class MyVisitorImplementation : IVisitor
{
public void Visit(MyAcceptorImplementation item)
{
Console.WriteLine("Mine");
}
public void Visit(AnotherAcceptorImplementation item)
{
Console.WriteLine("Another");
}
}

For more useful examples and better explanation have a look at Visitor Pattern and Iterator Pattern

EDIT: Here an example using both, the visitor and Iterator. The iterator is just the logic how to move through your aggregate. It would make more sense with a hierarchical structure.

public VisitorExample2()
{
MyVisitorImplementation visitor = new MyVisitorImplementation();
List<object> myListToHide = GetList();

//Here you hide that the aggregate is a List<object>
ConcreteIterator i = new ConcreteIterator(myListToHide);

IAcceptor item = i.First();
while(item != null)
{
item.Accept(visitor);
item = i.Next();
}
//... do something with the result
}


Related Topics



Leave a reply



Submit