Skip to content

Commit

Permalink
#590 explanation for Acyclic Visitor
Browse files Browse the repository at this point in the history
  • Loading branch information
iluwatar committed Aug 1, 2020
1 parent 595086a commit 7ac8eba
Showing 1 changed file with 122 additions and 4 deletions.
126 changes: 122 additions & 4 deletions acyclic-visitor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,126 @@ tags:
---

## 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

![alt text](./etc/acyclic-visitor.png "Acyclic Visitor")

## Applicability

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.
Expand All @@ -24,6 +138,7 @@ This pattern can be used:
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.

## Consequences

The good:

* No dependency cycles between class hierarchies.
Expand All @@ -32,11 +147,14 @@ The good:

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.

## Related patterns
* [Visitor Pattern](../visitor/)

* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)

## 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)

0 comments on commit 7ac8eba

Please sign in to comment.