#590 explanation for Acyclic Visitor
This commit is contained in:
@ -9,12 +9,126 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
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.
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally here are the visitors in action.
|
||||||
|
|
||||||
|
```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.
|
||||||
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
This pattern can be used:
|
This pattern can be used:
|
||||||
|
|
||||||
* When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
|
* When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
|
||||||
@ -24,6 +138,7 @@ This pattern can be used:
|
|||||||
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
|
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
The good:
|
The good:
|
||||||
|
|
||||||
* No dependency cycles between class hierarchies.
|
* No dependency cycles between class hierarchies.
|
||||||
@ -32,11 +147,14 @@ The good:
|
|||||||
|
|
||||||
The bad:
|
The bad:
|
||||||
|
|
||||||
* Violates the principle of least surprise or Liskov's Substitution principle by showing that it can accept all visitors but actually only being interested in particular visitors.
|
* 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.
|
||||||
* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
|
* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
|
||||||
|
|
||||||
## Related patterns
|
## Related patterns
|
||||||
* [Visitor Pattern](../visitor/)
|
|
||||||
|
* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
* [Acyclic Visitor](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
|
|
||||||
|
* [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)
|
||||||
|
Reference in New Issue
Block a user