Allow new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the troublesome dependency cycles that are inherent to the GoF Visitor Pattern.
Real world example
We have a hierarchy of modem classes. The modems in this hierarchy need to be visited by an external algorithm based on filtering criteria (is it Unix or DOS compatible modem).
In plain words
Acyclic Visitor allows functions to be added to existing class hierarchies without modifying the hierarchies.
WikiWikiWeb says
The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
Programmatic Example
Here’s the Modem
hierarchy.
1public abstract class Modem {
2 public abstract void accept(ModemVisitor modemVisitor);
3}
4
5public class Zoom extends Modem {
6 ...
7 @Override
8 public void accept(ModemVisitor modemVisitor) {
9 if (modemVisitor instanceof ZoomVisitor) {
10 ((ZoomVisitor) modemVisitor).visit(this);
11 } else {
12 LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
13 }
14 }
15}
16
17public class Hayes extends Modem {
18 ...
19 @Override
20 public void accept(ModemVisitor modemVisitor) {
21 if (modemVisitor instanceof HayesVisitor) {
22 ((HayesVisitor) modemVisitor).visit(this);
23 } else {
24 LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
25 }
26 }
27}
Next we introduce the ModemVisitor
hierarchy.
1public interface ModemVisitor {
2}
3
4public interface HayesVisitor extends ModemVisitor {
5 void visit(Hayes hayes);
6}
7
8public interface ZoomVisitor extends ModemVisitor {
9 void visit(Zoom zoom);
10}
11
12public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
13}
14
15public class ConfigureForDosVisitor implements AllModemVisitor {
16 ...
17 @Override
18 public void visit(Hayes hayes) {
19 LOGGER.info(hayes + " used with Dos configurator.");
20 }
21 @Override
22 public void visit(Zoom zoom) {
23 LOGGER.info(zoom + " used with Dos configurator.");
24 }
25}
26
27public class ConfigureForUnixVisitor implements ZoomVisitor {
28 ...
29 @Override
30 public void visit(Zoom zoom) {
31 LOGGER.info(zoom + " used with Unix configurator.");
32 }
33}
Finally, here are the visitors in action.
1 var conUnix = new ConfigureForUnixVisitor();
2 var conDos = new ConfigureForDosVisitor();
3 var zoom = new Zoom();
4 var hayes = new Hayes();
5 hayes.accept(conDos);
6 zoom.accept(conDos);
7 hayes.accept(conUnix);
8 zoom.accept(conUnix);
Program output:
// Hayes modem used with Dos configurator.
// Zoom modem used with Dos configurator.
// Only HayesVisitor is allowed to visit Hayes modem
// Zoom modem used with Unix configurator.
This pattern can be used:
The good:
The bad: