Sunday, 18 January 2009

Visitor Pattern in Java

Visitor Pattern is a way of separating an algorithm from an object structure upon which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. Thus, using the visitor pattern helps conformance with the open/closed principle.

The Visitor must visit each element of the Composite that functionally is in a Traverser object.

Visitor is guided by the Traverser to gather state from all of the objects in the Composite.

Once the state has been gathered, the Client can have the Visitor perform various operations on the state. When new functionality is required, only the Visitor must be enhanced.

When to use Visitor Pattern:
1. Use Visitor Pattern when you want to add capabilities to a composite of objects and encapsulation is not important.

Visitor Benifits:
1. Allows you to add operations to a Composite structure without changing the structure iteself.
2. Adding new operations is relatively easy.
3. The code for operations performed by the Visitor is centralized.

Visitor Drawbacks:
1. Encapsulation of the Composite classes is broken when the Visitor is used.
2. Because the traversal function is involved, changes to the Composite structure are more difficult.

To implement the Visitor pattern, you create a Visitor interface for the visitor, and a Visitable interface for the collection to be visited. You then have concrete classes that implement the Visitor and Visitable interfaces.

Let us look at an example...


// Visitor
public interface JavaFeatureVisitor {
void visit(Collections collections);
void visit(Generics generics);
void visitJavaFeatures(Java java);
}

// Visitable
public interface JavaFeature {
void accept(JavaFeatureVisitor visitor);
}


// Concrete Visitables
class Collections implements JavaFeature{

private String name;

Collections(String name) {
this.name = name;
}
String getName() {
return this.name;
}
public void accept(JavaFeatureVisitor visitor) {
visitor.visit(this);
}
}


class Generics implements JavaFeature{

public void accept(JavaFeatureVisitor visitor) {
visitor.visit(this);
}
}

public class Java {

JavaFeature[] javaFeatures;

public JavaFeature [] getJavaFeatures(){
return javaFeatures.clone();
}
public Java() {
this.javaFeatures = new JavaFeature[]
{ new Collections(" List "), new Collections(" Set "),
new Collections(" Map ") ,new Generics()};
}
}

// Concrete Visitors
public class JavaFeatureReadVisitor implements JavaFeatureVisitor {

public void visit(Collections collections) {
System.out.println("Going through "+ collections.getName()
+ " Collections");
}
public void visit(Generics generics) {
System.out.println("Going through generics");
}
public void visitJavaFeatures(Java java) {
System.out.println("\nLearning Java");
for(JavaFeature javaFeature : java.getJavaFeatures()) {
javaFeature.accept(this);
}
System.out.println("Got an Idea of Java Language");
}
}

public class JavaFeatureImplVisitor implements JavaFeatureVisitor {

public void visit(Collections collections) {
System.out.println("using Collections "+ collections.getName());
}
public void visit(Generics generics) {
System.out.println("using Generics");
}
public void visitJavaFeatures(Java java) {
System.out.println("\nImplementing Java Features");
for(JavaFeature javaFeature : java.getJavaFeatures()) {
javaFeature.accept(this);
}
System.out.println("Very useful features");
}

}


public class VisitorDemo {
static public void main(String[] args){
Java java = new Java();
JavaFeatureVisitor learnJavaFeature = new JavaFeatureReadVisitor();
JavaFeatureVisitor implJavaFeature = new JavaFeatureImplVisitor();
learnJavaFeature.visitJavaFeatures(java);
implJavaFeature.visitJavaFeatures(java);
}
}



Hope it will be useful...

3 comments:

Prashant Jalasutram said...

Sneha,

Nice explanation and i liked the example very much.

Thanks
Prashant

Ji said...

Could you customize your blog to allow followers option or RSS feed option ?

Thanks

Ajay said...

Can u pl explain Double Dispatch ?