Discussions

Overview:
The Compositional Visitor replaces the Visitor class hierarchy with a compositional pattern where each Visitable object has its own (very simple) Visitor interface - and each Visitor picks and chooses which Visitor interfaces it implements and hence which elements it operates on. If a Visitor does not need to process a particular element then it does not implement its associated Visitor interface. Knowledge of the standard Visitor Pattern is assumed.
Background:
The traditional Visitor pattern is flawed for many real world applications as it scales badly. As it says in Design Patterns:
Consequences (of the Visitor Pattern):
3. Adding new ConcreteElement classes is hard.... each new ConcreteElement gives rise to a new abstract operation on Visitor and a corresponding implementation in each ConcreteVisitor class.
You are left with two choices either:
a) You have a separate Visitor interfaces (or class hierarchies) for each 'type' of Visitor. (Visitors of the same 'type' being Visitors that process the same elements) - and a corresponding Visitable interface for each 'type', or
b) (The orthodox approach) Implement visit(element) for every possible (Visitable) element in every Visitor.
Neither way is elegant. It is possible to have a class hierarchy of Visitors with a superclass that implements default implementations of visit for every Visitable element; but this is far from ideal as that superclass still needs to be maintained and the Visitors are aligned in a hierarchy based on the fact that they are Visitors not based on their role or requirements within the application.
In essence the Visitors and Visitable objects should be INDEPENDENT - so the Visitable objects should not care if a new Visitor is added - and a Visitor should not care if a new Visitable object is added. Furthermore the Visitors themselves should be independent - unless they share common processing requirements.
Compositional Visitor Pattern:
The secret to making the Visitors and Visitable objects independent is to use composition. There is only one visitable interface:
public interface Visitable {
public Object accept(Object visitor) throws Exception;
}
Each visitable element has its own Visitor interface (which could potentially be created using code generation as they are very simple) e.g.
public interface Element1Visitor {
public Object visit(Element1 visitable) throws Exception;
}
public interface Element2Visitor {
public Object visit(Element2 visitable) throws Exception;
}
Each Visitable element implements Visitable. The accept method checks to see if the visitor implements its Visitor - if so then is passes itself to the Visitor for processing - if not then it either passes the request up the class hierarchy (if the superclass implements Visitable) or returns null (or conceivably throws a checked NotProcessableByVisitorException).
public class Element1 implements Visitable {
//Other properties and methods
public Object accept(Object visitor) throws Exception {
if (visitor instanceof Element1Visitor) {
return ((Element1Visitor)visitor).visit(this);
}
else {
return null;
//For subclasses of Visitable classes
// return super.accept(visitor)
}
}
}
public class Element2 implements Visitable{
//Other properties and methods
public Object accept(Object visitor) throws Exception {
if (visitor instanceof Element2Visitor) {
return ((Element2Visitor)visitor).visit(this);
}
else {
return null;
//For subclasses of Visitable classes
// return super.accept(visitor)
}
}
}
The Visitor itself simply implements the Visitor interfaces for the Visitable Elements it can process:
public class SampleVisitor implements Element1Visitor, Element2Visitor {
public Object visit(Element1 visitable) throws Exception {
Object result = null;
//Do work for element1
return result;
}
public Object visit(Element2 visitable) throws Exception {
Object result = null;
//Do work for element2
return result;
}
}
The Visitor can then be used as normal:
SampleVisitor sampleVisitor = new SampleVisitor();
for (Visitable visitable : visitableObjects){
visitable.accept(sampleVisitor);
}
Conclusion
Whilst this pattern does create some additional classes (the extra Visitor interfaces) - they are simple and could be automatically generated. It also has a more complex accept method than just a straight callback to the Visitor; but it is far from complicated and only needs to be written once to handle all possible Visitors. The big benefit, on the other hand, is that you can use the Visitor pattern without worrying about adding new Visitable elements or Visitors. In fact ANY class can become a visitor just by implementing the appropriate interfaces for whatever elements you want to process. Visitors become cheap and easy.
But why use the Visitor pattern anyway? Because Visitors are a great way to centralise specific processing and remove logic (especially business logic) from model objects. This is a boon for Test Driven Development because
a) It assists with keeping certain classes (e.g. model objects) clean and free from processing logic so they can then be used freely throughout test code without worrying about mocking them or about their behaviour changing.
b) The processing logic is all in one spot (the Visitor) and hence can be more easily, and neatly, tested than if it were scattered throughout the Visitable entities.
I believe that in general putting logic within model classes is bad idea - and as soon as other system resources are required (e.g. a Rules Engine) you cannot put the logic there. Visitors offer - to my mind - a much more elegant solution.

I like this idea except for the instanceof checks.
What about having a method on the Vistable interface like this:
public interface Visitable {
Object accept(Object visitor) throws Exception;
Class visitorType();
}
Then the Vistor can determine whether they are able to visit the visitable before calling it. Then the Visitable can just cast to the right type. With generics, the cast can also be removed.

There are many ways to skin the proverbial cat here - I chose the instanceof solution for my example as it is the simplest to understand and leaves the interfaces looking like the standard Visitor pattern ones. The 'interesting' implementation of generics used in Java prevents you having a generic Visitor interface unfortunately. MyVisitor implements Visitor, Visitor causes a compilation error because type erasure makes the two interfaces look identical. (Queue much growling).
Having the visitor decide whether it can be processed by the visitee is an interesting option though. You may still need to consider checking the superclass as well - it may be that visitor implements the interface corresponding to the superclass rather than the actual object class.
Either way - the fundimental point of the pattern - composing the visitor from multiple visitor interfaces remains the same.

There are many ways to skin the proverbial cat here - I chose the instanceof solution for my example as it is the simplest to understand and leaves the interfaces looking like the standard Visitor pattern ones. The 'interesting' implementation of generics used in Java prevents you having a generic Visitor interface unfortunately. MyVisitor implements Visitor, Visitor causes a compilation error because type erasure makes the two interfaces look identical. (Queue much growling).

You'd have to use explicit casts and ignore the warnings on the visitor side. The benefit is only on the visitee side. This is unfortunately what we have to work with, I'm done growling about it.

Having the visitor decide whether it can be processed by the visitee is an interesting option though. You may still need to consider checking the superclass as well - it may be that visitor implements the interface corresponding to the superclass rather than the actual object class.

You can use the isAssignable() method in Class. The other thing this allows for is an easy way to implement a true compositional visitor. That is, the visitor doesn't actually implement the visitee interfaces but is composed of individual visitor instances that do. I'll put some code up later.
I love the concept. I think this could be a really useful thing. At least in Java. You could also probably use this to implement a Compositional Builder too. I was going to mention before that in yor original post you seem to be talking about the Builder pattern more than the visitor. The visitor pattern is usually used to emulate multiple-dispatch

Hello, thx everyone for all the inputs, anyway as a side note, you should now that using instanceof is way more faster than using Class.isAssignableFrom(Class class) or other method in Class.isInstance(Object object).
If we want an exact class match, just class reference equality (as the object's class reference should be located in the same place) :
if(visitor.getClass() == Element2Visitor.class) {
//...
}

TechTarget provides technology professionals with the information they need to perform their jobs - from developing strategy, to making cost-effective purchase decisions and managing their organizations technology projects - with its network of technology-specific websites, events and online magazines.