The Visitor design pattern is one of the classic design patterns covered by the Design Patterns: Elements of Reusable Object-Oriented Software book. This books sets the foundation for the classic design patterns software developers are usually familiar with.
How to Learn Design Patterns
There are many resources for learning design patterns. You can find various books, video clips and blogs that cover this topic. Nevertheless, many of them are a bit inaccurate in their texts compared with the Design Patterns: Elements of Reusable Object-Oriented Software book. Therefore, it is always recommended to keep a copy of that book and use it as the ultimate reference when learning this topic.
The Visitor Design Pattern
The Visitor design pattern provides a solution for the following programming problem: How to add functionality to existing objects without changing the code of the classes they were created from? When dealing with Python and JavaScript we can easily and dynamically add new functions to objects that already exist. When dealing with C#, Kotlin, and Scala, we can easily define new functions as an extension for a specific type, and by doing so, add more operations (functionalities) we will be able to perform on objects instantiated from that type.
The following code sample includes the abstract class Element, and the XMLElement and the JSONElement classes that extend it.
public abstract class Element {
public abstract void accept(Visitor visitor);
}
public class JSONElement extends Element {
private int id;
public JSONElement(int id) {
this.setId(id);
}
public void accept(Visitor v) {
v.visit(this);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
public class XMLElement extends Element{
private int id;
public XMLElement(int id) {
this.setId(id);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
//...
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
The Element abstract class includes the definition of the accept() method. This method takes a Visitor object. Visitor is an interfaces, implemented by classes. Each class that implements Visitor describes a specific operation. We can implement Visitor as many times as we want.
public interface Visitor {
public void visit(Element element);
}
public class ElementCheckingIDVisitor implements Visitor {
public void visit(XMLElement xe) {
System.out.println(
"processing an XML element " + xe.getId());
System.out.println("checking the id is OK");
if(xe.getId()>99 && xe.getId()<100000) {
System.out.println("id is ok");
} else {
System.out.println("id is not ok");
}
}
public void visit(JSONElement je) {
System.out.println(
"processing a JSON element " + je.getId());
System.out.println("checking the id is OK");
if(je.getId()>99 && je.getId()<100000) {
System.out.println("id is ok");
} else {
System.out.println("id is not ok");
}
}
@Override
public void visit(Element element) {
if(element instanceof JSONElement) {
visit((JSONElement) element);
} else if(element instanceof XMLElement) {
visit((XMLElement) element);
}
}
}
public class ElementMultiplyingIDBy10Visitor implements Visitor {
public void visit(XMLElement xe) {
System.out.println(
"processing an XML element " + xe.getId());
xe.setId(10*xe.getId());
System.out.println("the new id is " + xe.getId());
}
public void visit(JSONElement je) {
System.out.println(
"processing a JSON element " + je.getId());
je.setId(10*je.getId());
System.out.println("the new id is " + je.getId());
}
@Override
public void visit(Element element) {
if(element instanceof JSONElement) {
visit((JSONElement) element);
} else if(element instanceof XMLElement) {
visit((XMLElement) element);
}
}
}
In order to get a good demo for the Visitor design pattern, let’s create a list of Element objects and use a Visitor object we can use to visit each one of the Element object using a simple loop. The Visitor object visits an Element object when the accept method is invoked on the Element object and the reference for the Visitor object is passed over to it. Inside the accept method we can invoke the visit method on the Visitor object passing over the reference for the Element object.
public class Document extends Element {
public List elements = new ArrayList<>();
@Override
public void accept(Visitor v) {
for (Element e : this.elements) {
e.accept(v);
}
}
}
public class VisitorDemo {
public static void main(String[] args) {
//Visitor v = new ElementCheckingIDVisitor();
Visitor v = new ElementMultiplyingIDBy10Visitor();
Document d = new Document();
d.elements.add(new JSONElement(534));
d.elements.add(new JSONElement(234));
d.elements.add(new XMLElement(9789));
d.elements.add(new XMLElement(7877));
d.elements.add(new JSONElement(645));
d.accept(v);
d.accept(v);
}
}
Professional Training
We, at life michael, continuously develop new professional seminars for software developers in various topics, including the design patterns topic. Most of the classic design patterns can be implemented (nearly) in every programming language. As a result, we chose to develop dedicated separated seminars for learning this topic in various programming languages, such as the Design Patterns in Python, Design Patterns in Kotlin, Design Patterns in Java, and the Design Patterns in C#. Apart of the dedicated seminars for learning the classic design patterns, we also continuously develop professional seminars that cover design patterns beyond the classic ones, such as the Combinator Design Pattern as well as many other related seminars, such as a seminar about Anti Patterns and a seminar about the SOLID Design Principles.
We at life michael, continuously develop new professional seminars and new professional courses. You can find detailed information about the courses and the seminars we developed so far at life michael.