2018-05-27 17:10:20 +03:00
---
layout: pattern
title: Acyclic Visitor
folder: acyclic-visitor
permalink: /patterns/acyclic-visitor/
categories: Behavioral
2021-05-19 10:49:05 -06:00
language: en
2018-05-27 17:10:20 +03:00
tags:
2019-12-13 21:09:28 +02:00
- Extensibility
2018-05-27 17:10:20 +03:00
---
## Intent
2020-08-01 15:18:32 +03:00
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.
## Explanation
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 ](https://wiki.c2.com/?AcyclicVisitor ) 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.
```java
public abstract class Modem {
public abstract void accept(ModemVisitor modemVisitor);
}
public class Zoom extends Modem {
...
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof ZoomVisitor) {
((ZoomVisitor) modemVisitor).visit(this);
} else {
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
}
}
}
public class Hayes extends Modem {
...
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof HayesVisitor) {
((HayesVisitor) modemVisitor).visit(this);
} else {
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
}
}
}
```
Next we introduce the `ModemVisitor` hierarchy.
```java
public interface ModemVisitor {
}
public interface HayesVisitor extends ModemVisitor {
void visit(Hayes hayes);
}
public interface ZoomVisitor extends ModemVisitor {
void visit(Zoom zoom);
}
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
}
public class ConfigureForDosVisitor implements AllModemVisitor {
...
@Override
public void visit(Hayes hayes) {
LOGGER.info(hayes + " used with Dos configurator.");
}
@Override
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Dos configurator.");
}
}
public class ConfigureForUnixVisitor implements ZoomVisitor {
...
@Override
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Unix configurator.");
}
}
```
2020-08-04 21:45:16 +03:00
Finally, here are the visitors in action.
2020-08-01 15:18:32 +03:00
```java
var conUnix = new ConfigureForUnixVisitor();
var conDos = new ConfigureForDosVisitor();
var zoom = new Zoom();
var hayes = new Hayes();
hayes.accept(conDos);
zoom.accept(conDos);
hayes.accept(conUnix);
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.
```
2018-05-27 17:10:20 +03:00
2019-12-07 20:01:13 +02:00
## Class diagram
2020-08-01 15:18:32 +03:00
2019-12-07 20:01:13 +02:00

2018-05-27 17:10:20 +03:00
## Applicability
2020-08-01 15:18:32 +03:00
2018-05-27 17:10:20 +03:00
This pattern can be used:
2019-12-13 09:36:57 +02:00
2018-05-27 17:10:20 +03:00
* When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
* When there are functions that operate upon a hierarchy, but which do not belong in the hierarchy itself. e.g. the ConfigureForDOS / ConfigureForUnix / ConfigureForX issue.
* When you need to perform very different operations on an object depending upon its type.
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
2021-10-24 11:23:12 +05:00
## Tutorial
* [Acyclic Visitor Pattern Example ](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html )
2018-06-14 17:56:14 +03:00
## Consequences
2020-08-01 15:18:32 +03:00
2018-06-14 17:56:14 +03:00
The good:
2019-12-13 09:36:57 +02:00
2018-06-14 17:56:14 +03:00
* No dependency cycles between class hierarchies.
* No need to recompile all the visitors if a new one is added.
* Does not cause compilation failure in existing visitors if class hierarchy has a new member.
The bad:
2019-12-13 09:36:57 +02:00
2020-08-01 15:18:32 +03:00
* Violates [Liskov's Substitution Principle ](https://java-design-patterns.com/principles/#liskov-substitution-principle ) by showing that it can accept all visitors but actually only being interested in particular visitors.
2018-06-14 17:56:14 +03:00
* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
2018-05-27 17:10:20 +03:00
## Related patterns
2020-08-01 15:18:32 +03:00
* [Visitor Pattern ](https://java-design-patterns.com/patterns/visitor/ )
2018-05-27 17:10:20 +03:00
## Credits
2020-08-01 15:18:32 +03:00
* [Acyclic Visitor by Robert C. Martin ](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf )
* [Acyclic Visitor in WikiWikiWeb ](https://wiki.c2.com/?AcyclicVisitor )