📢 This article was translated by gemini-2.5-flash
Visitor Pattern: A Behavioral Design Pattern
Intent
Represents an operation to be performed on elements of an object structure. It lets you define a new operation without changing the classes of the elements on which it operates.
Structure

Key components:
- Visitor Declares a
Visit operation for each class of ConcreteElement in the object structure. The operation’s name uniquely identifies the class sending the Visit request, allowing the visitor to determine the specific class of the element being visited. This lets the visitor access the element directly through its specific interface. - ConcreteVisitor Implements each operation declared by
Visitor. Each operation implements a part of the algorithm, specific to the class of the object in the structure. ConcreteVisitor provides context for the algorithm and stores its local state, which is often accumulated during structure traversal. - Element Defines an
Accept operation that takes a visitor as an argument. - ConcreteElement Implements the
Accept operation, taking a visitor as an argument. - ObjectStructure Can enumerate its elements. It provides a high-level interface allowing the visitor to access its elements. It can be a composite or a collection, like a list or an unordered set.
Applicability
The Visitor pattern is suitable when:
- An object structure contains many classes of objects with different interfaces, and you want to perform operations on these objects that depend on their concrete classes.
- You need to perform many different, unrelated operations on objects in an object structure, but want to avoid “polluting” their classes with these operations. Visitor allows you to centralize related operations into a single class. When the object structure is shared by many applications, using the Visitor pattern lets each application only include the operations it needs.
- The classes defining the object structure rarely change, but you frequently need to define new operations on this structure. Changing an object structure class requires redefining the interface for all visitors, which can be costly. If object structure classes change frequently, it might be better to define these operations directly within those classes.
Example 1
In a library management system, two types of literature are managed: books and articles. The requirement is to calculate the total number of pages for all collected literature using the Visitor pattern. The class diagram is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
| interface LibraryVisitor{
void visit(Book p_book);
void visit(Article p_article);
void printSum();
}
class LibrarySumPrintVisitor implements LibraryVisitor{ // Prints total page count
private int sum = 0;
@Override
public void visit(Book p_book) {
sum = sum + p_book.getNumberOfPages();
}
@Override
public void visit(Article p_article) {
sum = sum + p_article.getNumberOfPages();
}
@Override
public void printSum() {
System.out.println("SUM = " + sum);
}
}
interface LibraryItemInterface{
void accept(LibraryVisitor visitor);
}
class Article implements LibraryItemInterface{
private String m_title; // Article title
private String m_author; // Article author
private int m_start_page;
private int m_end_page;
public Article(String p_author, String p_title, int p_start_page, int p_end_page){
m_title = p_title;
m_author = p_author;
m_start_page = p_start_page;
m_end_page = p_end_page;
}
public int getNumberOfPages(){
return m_end_page - m_start_page;
}
@Override
public void accept(LibraryVisitor visitor) {
visitor.visit(this);
}
}
class Book implements LibraryItemInterface{
private String m_title; // Book title
private String m_author; // Book author
private int m_pages; // Page count
public Book(String p_author, String p_title, int p_pages){
m_title = p_title;
m_author = p_author;
m_pages = p_pages;
}
public int getNumberOfPages(){
return m_pages;
}
@Override
public void accept(LibraryVisitor visitor) {
visitor.visit(this);
}
}
|
Example 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
| import java.util.ArrayList;
import java.util.List;
public class VisitorPattern {
public static void main(String[] args) {
PersonStructure personStructure = new PersonStructure();
Visitor1 visitor1 = new Visitor1();
System.out.println("For Visitor1");
personStructure.Accept(visitor1);
System.out.println("The sum of student age: " + visitor1.getStudentAgeSum());
System.out.println("The sum of teacher age: " + visitor1.getTeacherAgeSum());
Visitor2 visitor2 = new Visitor2();
System.out.println("For Visitor2");
personStructure.Accept(visitor2);
System.out.println("Max score: " + visitor2.getMaxScore());
System.out.println("Max work year: " + visitor2.getMaxWorkYear());
}
}
interface Visitor{
public void VisitS(Student student);
public void VisitT(Teacher teacher);
}
class Visitor1 implements Visitor{// Calculate the sum of student ages and teacher ages separately
private int studentAgeSum = 0;
private int teacherAgeSum = 0;
public int getStudentAgeSum() {
return studentAgeSum;
}
public int getTeacherAgeSum() {
return teacherAgeSum;
}
@Override
public void VisitS(Student student){
System.out.println("Visitor1: " + student.getName() + " Age: " + student.getAge());
studentAgeSum += student.getAge();
}
public void VisitT(Teacher teacher){
System.out.println("Visitor1: " + teacher.getName() + " Age: " + teacher.getAge());
teacherAgeSum += teacher.getAge();
}
}
class Visitor2 implements Visitor{ // Find the highest student score and highest teacher work year separately
private int maxScore = -1;
private int maxWorkYear = -1;
public int getMaxScore() {
return maxScore;
}
public int getMaxWorkYear() {
return maxWorkYear;
}
@Override
public void VisitS(Student student){
System.out.println("Visitor2: " + student.getName() + " Score: " + student.getScore());
if(student.getScore() > maxScore) maxScore = student.getScore();
// maxScore = Math.max(maxScore, student.getScore());
}
public void VisitT(Teacher teacher){
System.out.println("Visitor2: " + teacher.getName() + " WorkYear: " + teacher.getWorkYear());
if(teacher.getWorkYear() > maxWorkYear) maxWorkYear = teacher.getWorkYear();
// maxWorkYear = Math.max(maxWorkYear, teacher.getWorkYear());
}
}
class PersonStructure{
private List<Person> personList = new ArrayList<>();
public PersonStructure(){
personList.add(new Student("Mike", 16, 99));
personList.add(new Student("Jane", 15, 100));
personList.add(new Teacher("Alice mana", 20, 1));
}
public void Accept(Visitor visitor){
for(Person p : personList){
p.Accept(visitor);
}
}
}
abstract class Person{
private String name;
private int age;
public abstract void Accept(Visitor visitor);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student extends Person{
private int score;
public int getScore() {
return score;
}
public Student(String name, int age, int score){
this.setName(name);
this.setAge(age);
this.score = score;
}
@Override
public void Accept(Visitor visitor){
visitor.VisitS(this);
}
}
class Teacher extends Person{
private int workYear;
public int getWorkYear() {
return workYear;
}
public Teacher(String name, int age, int workYear){
this.setName(name);
this.setAge(age);
this.workYear = workYear;
}
@Override
public void Accept(Visitor visitor){
visitor.VisitT(this);
}
}
|