Visitor
The Visitor pattern lets you add operations to objects without having to modify these objects -- in other words, it's a way of separating an algorithm from an object structure on which it operates. This pattern is used when:
- you have a complex structure that contains objects of different types,
- similar operations need to be performed on all these objects.
Visitor defines behavior in abstract classes or interfaces. This helps avoid the use of conditional blocks and type checks, which tend to become clunky and hard to maintain.
Below is a code sample where a sports fan (visitor) wants to visit the Olympic Games to watch competitions in various sports (visitees). All visitees have the Accept
method that takes a parameter representing a visitor:
1class SummerOlympicSport {
2 accept(visitor) { }
3}
4
5class DivingCompetition extends SummerOlympicSport {
6 jump() { }
7 accept(visitor) { visitor.watchDiving(this); }
8}
9
10class CyclingTrackRace extends SummerOlympicSport {
11 ride() { }
12 accept(visitor) { visitor.watchCycling(this); }
13}
14
15class BadmintonMatch extends SummerOlympicSport {
16 hit() { }
17 accept(visitor) { visitor.watchBadminton(this); }
18}
Here's how the visitor is implemented:
1// In JavaScript
2class ICompetitionVisitor {
3 watchDiving(divingCompetition) {}
4 watchCycling(cyclingTrackRace) {}
5 watchBadminton(badmintonMatch) {}
6}
7
8class CompetitionVisitor extends ICompetitionVisitor {
9 watchBadminton(badmintonMatch) {
10 badmintonMatch.hit();
11 }
12
13 watchCycling(cyclingTrackRace) {
14 cyclingTrackRace.ride();
15 }
16
17 watchDiving(divingCompetition) {
18 divingCompetition.jump();
19 }
20}
At call site, we can now initialize a visitor (competitionVisitor
), and walk them through the list of visitees (competitionsToVisit
) in a loop where we call the Accept()
method that hides dissimilarity of the visitees:
1let competitionsToVisit = [
2 new DivingCompetition(),
3 new BadmintonMatch(),
4 new CyclingTrackRace()
5];
6
7let competitionVisitor = new CompetitionVisitor();
8
9for (let competition of competitionsToVisit) {
10 competition.accept(competitionVisitor);
11}
There's a related pattern called Iterator. However, Iterator is used on collections that contain objects of the same type. Visitor has wider applicability and can be used on hierarchical and/or composite data structures.