235 lines
6.7 KiB
Markdown
235 lines
6.7 KiB
Markdown
---
|
|
layout: pattern
|
|
title: Visitor
|
|
folder: visitor
|
|
permalink: /patterns/visitor/
|
|
categories: Behavioral
|
|
language: en
|
|
tags:
|
|
- Gang of Four
|
|
---
|
|
|
|
## Intent
|
|
|
|
Represent an operation to be performed on the elements of an object structure. Visitor lets you
|
|
define a new operation without changing the classes of the elements on which it operates.
|
|
|
|
## Explanation
|
|
|
|
Real-world example
|
|
|
|
> Consider a tree structure with army units. Commander has two sergeants under it and each sergeant
|
|
> has three soldiers under them. Given that the hierarchy implements the visitor pattern, we can
|
|
> easily create new objects that interact with the commander, sergeants, soldiers, or all of them.
|
|
|
|
In plain words
|
|
|
|
> Visitor pattern defines operations that can be performed on the nodes of the data structure.
|
|
|
|
Wikipedia says
|
|
|
|
> In object-oriented programming and software engineering, the visitor design pattern is a way of
|
|
> separating an algorithm from an object structure on which it operates. A practical result of this
|
|
> separation is the ability to add new operations to existing object structures without modifying
|
|
> the structures.
|
|
|
|
**Programmatic Example**
|
|
|
|
Given the army unit example from above, we first have the Unit and UnitVisitor base types.
|
|
|
|
```java
|
|
public abstract class Unit {
|
|
|
|
private final Unit[] children;
|
|
|
|
public Unit(Unit... children) {
|
|
this.children = children;
|
|
}
|
|
|
|
public void accept(UnitVisitor visitor) {
|
|
Arrays.stream(children).forEach(child -> child.accept(visitor));
|
|
}
|
|
}
|
|
|
|
public interface UnitVisitor {
|
|
|
|
void visitSoldier(Soldier soldier);
|
|
|
|
void visitSergeant(Sergeant sergeant);
|
|
|
|
void visitCommander(Commander commander);
|
|
}
|
|
```
|
|
|
|
Then we have the concrete units.
|
|
|
|
```java
|
|
public class Commander extends Unit {
|
|
|
|
public Commander(Unit... children) {
|
|
super(children);
|
|
}
|
|
|
|
@Override
|
|
public void accept(UnitVisitor visitor) {
|
|
visitor.visitCommander(this);
|
|
super.accept(visitor);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "commander";
|
|
}
|
|
}
|
|
|
|
public class Sergeant extends Unit {
|
|
|
|
public Sergeant(Unit... children) {
|
|
super(children);
|
|
}
|
|
|
|
@Override
|
|
public void accept(UnitVisitor visitor) {
|
|
visitor.visitSergeant(this);
|
|
super.accept(visitor);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "sergeant";
|
|
}
|
|
}
|
|
|
|
public class Soldier extends Unit {
|
|
|
|
public Soldier(Unit... children) {
|
|
super(children);
|
|
}
|
|
|
|
@Override
|
|
public void accept(UnitVisitor visitor) {
|
|
visitor.visitSoldier(this);
|
|
super.accept(visitor);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "soldier";
|
|
}
|
|
}
|
|
```
|
|
|
|
Here are then some concrete visitors.
|
|
|
|
```java
|
|
@Slf4j
|
|
public class CommanderVisitor implements UnitVisitor {
|
|
|
|
@Override
|
|
public void visitSoldier(Soldier soldier) {
|
|
// Do nothing
|
|
}
|
|
|
|
@Override
|
|
public void visitSergeant(Sergeant sergeant) {
|
|
// Do nothing
|
|
}
|
|
|
|
@Override
|
|
public void visitCommander(Commander commander) {
|
|
LOGGER.info("Good to see you {}", commander);
|
|
}
|
|
}
|
|
|
|
@Slf4j
|
|
public class SergeantVisitor implements UnitVisitor {
|
|
|
|
@Override
|
|
public void visitSoldier(Soldier soldier) {
|
|
// Do nothing
|
|
}
|
|
|
|
@Override
|
|
public void visitSergeant(Sergeant sergeant) {
|
|
LOGGER.info("Hello {}", sergeant);
|
|
}
|
|
|
|
@Override
|
|
public void visitCommander(Commander commander) {
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
@Slf4j
|
|
public class SoldierVisitor implements UnitVisitor {
|
|
|
|
@Override
|
|
public void visitSoldier(Soldier soldier) {
|
|
LOGGER.info("Greetings {}", soldier);
|
|
}
|
|
|
|
@Override
|
|
public void visitSergeant(Sergeant sergeant) {
|
|
// Do nothing
|
|
}
|
|
|
|
@Override
|
|
public void visitCommander(Commander commander) {
|
|
// Do nothing
|
|
}
|
|
}
|
|
```
|
|
|
|
Finally, we can show the power of visitors in action.
|
|
|
|
```java
|
|
commander.accept(new SoldierVisitor());
|
|
commander.accept(new SergeantVisitor());
|
|
commander.accept(new CommanderVisitor());
|
|
```
|
|
|
|
Program output:
|
|
|
|
```
|
|
Greetings soldier
|
|
Greetings soldier
|
|
Greetings soldier
|
|
Greetings soldier
|
|
Greetings soldier
|
|
Greetings soldier
|
|
Hello sergeant
|
|
Hello sergeant
|
|
Good to see you commander
|
|
```
|
|
|
|
## Class diagram
|
|
|
|

|
|
|
|
## Applicability
|
|
|
|
Use the Visitor pattern when
|
|
|
|
* An object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes.
|
|
* Many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them.
|
|
* The classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes.
|
|
|
|
## Tutorials
|
|
|
|
* [Refactoring Guru](https://refactoring.guru/design-patterns/visitor)
|
|
* [Dzone](https://dzone.com/articles/design-patterns-visitor)
|
|
* [Sourcemaking](https://sourcemaking.com/design_patterns/visitor)
|
|
|
|
## Known uses
|
|
|
|
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
|
|
* [javax.lang.model.element.AnnotationValue](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValue.html) and [AnnotationValueVisitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValueVisitor.html)
|
|
* [javax.lang.model.element.Element](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html) and [Element Visitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/ElementVisitor.html)
|
|
* [java.nio.file.FileVisitor](http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitor.html)
|
|
|
|
## Credits
|
|
|
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
|
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
|
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|