Skip to content

Commit 7ac8eba

Browse files
committed
iluwatar#590 explanation for Acyclic Visitor
1 parent 595086a commit 7ac8eba

1 file changed

Lines changed: 122 additions & 4 deletions

File tree

acyclic-visitor/README.md

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,126 @@ tags:
99
---
1010

1111
## Intent
12-
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.
12+
13+
Allow new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating
14+
the troublesome dependency cycles that are inherent to the GoF Visitor Pattern.
15+
16+
## Explanation
17+
18+
Real world example
19+
20+
> We have a hierarchy of modem classes. The modems in this hierarchy need to be visited by an external algorithm based
21+
> on filtering criteria (is it Unix or DOS compatible modem).
22+
23+
In plain words
24+
25+
> Acyclic Visitor allows functions to be added to existing class hierarchies without modifying the hierarchies.
26+
27+
[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says
28+
29+
> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those
30+
> hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
31+
32+
**Programmatic Example**
33+
34+
Here's the `Modem` hierarchy.
35+
36+
```java
37+
public abstract class Modem {
38+
public abstract void accept(ModemVisitor modemVisitor);
39+
}
40+
41+
public class Zoom extends Modem {
42+
...
43+
@Override
44+
public void accept(ModemVisitor modemVisitor) {
45+
if (modemVisitor instanceof ZoomVisitor) {
46+
((ZoomVisitor) modemVisitor).visit(this);
47+
} else {
48+
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
49+
}
50+
}
51+
}
52+
53+
public class Hayes extends Modem {
54+
...
55+
@Override
56+
public void accept(ModemVisitor modemVisitor) {
57+
if (modemVisitor instanceof HayesVisitor) {
58+
((HayesVisitor) modemVisitor).visit(this);
59+
} else {
60+
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
61+
}
62+
}
63+
}
64+
```
65+
66+
Next we introduce the `ModemVisitor` hierarchy.
67+
68+
```java
69+
public interface ModemVisitor {
70+
}
71+
72+
public interface HayesVisitor extends ModemVisitor {
73+
void visit(Hayes hayes);
74+
}
75+
76+
public interface ZoomVisitor extends ModemVisitor {
77+
void visit(Zoom zoom);
78+
}
79+
80+
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
81+
}
82+
83+
public class ConfigureForDosVisitor implements AllModemVisitor {
84+
...
85+
@Override
86+
public void visit(Hayes hayes) {
87+
LOGGER.info(hayes + " used with Dos configurator.");
88+
}
89+
@Override
90+
public void visit(Zoom zoom) {
91+
LOGGER.info(zoom + " used with Dos configurator.");
92+
}
93+
}
94+
95+
public class ConfigureForUnixVisitor implements ZoomVisitor {
96+
...
97+
@Override
98+
public void visit(Zoom zoom) {
99+
LOGGER.info(zoom + " used with Unix configurator.");
100+
}
101+
}
102+
```
103+
104+
Finally here are the visitors in action.
105+
106+
```java
107+
var conUnix = new ConfigureForUnixVisitor();
108+
var conDos = new ConfigureForDosVisitor();
109+
var zoom = new Zoom();
110+
var hayes = new Hayes();
111+
hayes.accept(conDos);
112+
zoom.accept(conDos);
113+
hayes.accept(conUnix);
114+
zoom.accept(conUnix);
115+
```
116+
117+
Program output:
118+
119+
```
120+
// Hayes modem used with Dos configurator.
121+
// Zoom modem used with Dos configurator.
122+
// Only HayesVisitor is allowed to visit Hayes modem
123+
// Zoom modem used with Unix configurator.
124+
```
13125

14126
## Class diagram
127+
15128
![alt text](./etc/acyclic-visitor.png "Acyclic Visitor")
16129

17130
## Applicability
131+
18132
This pattern can be used:
19133

20134
* 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:
24138
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
25139

26140
## Consequences
141+
27142
The good:
28143

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

33148
The bad:
34149

35-
* 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.
150+
* 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.
36151
* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
37152

38153
## Related patterns
39-
* [Visitor Pattern](../visitor/)
154+
155+
* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)
40156

41157
## Credits
42-
* [Acyclic Visitor](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
158+
159+
* [Acyclic Visitor by Robert C. Martin](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
160+
* [Acyclic Visitor in WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor)

0 commit comments

Comments
 (0)