Visitor
Propósito
Representar una operación a realizar sobre los elementos de una estructura de objetos. Visitante permite
definir una nueva operación sin cambiar las clases de los elementos sobre los que opera.
Explicación
Ejemplo del mundo real
Considere una estructura de árbol con unidades del ejército. El comandante tiene dos sargentos bajo él y cada sargento
tiene tres soldados bajo él. Dado que la jerarquía implementa el patrón visitante, podemos
crear fácilmente nuevos objetos que interactúen con el comandante, los sargentos, los soldados, o todos ellos.
Ejemplo del mundo real
El patrón Visitante define las operaciones que se pueden realizar en los nodos de la estructura de datos.
Wikipedia dice
En programación orientada a objetos e ingeniería de software, el patrón de diseño visitante es una forma de
separar un algoritmo de una estructura de objetos sobre la que opera. Un resultado práctico de esta
separación es la capacidad de añadir nuevas operaciones a estructuras de objetos existentes sin modificarlas.
las estructuras.
Ejemplo programático
Dado el ejemplo anterior de la unidad del ejército, primero tenemos los tipos básicos Unit y UnitVisitor.
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 visit(Soldier soldier);
void visit(Sergeant sergeant);
void visit(Commander commander);
}
Luego tenemos las unidades de hormigón.
public class Commander extends Unit {
public Commander(Unit... children) {
super(children);
}
@Override
public void accept(UnitVisitor visitor) {
visitor.visit(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.visit(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.visit(this);
super.accept(visitor);
}
@Override
public String toString() {
return "soldier";
}
}
He aquí, pues, algunos visitantes concretos.
@Slf4j
public class CommanderVisitor implements UnitVisitor {
@Override
public void visit(Soldier soldier) {
// Do nothing
}
@Override
public void visit(Sergeant sergeant) {
// Do nothing
}
@Override
public void visit(Commander commander) {
LOGGER.info("Good to see you {}", commander);
}
}
@Slf4j
public class SergeantVisitor implements UnitVisitor {
@Override
public void visit(Soldier soldier) {
// Do nothing
}
@Override
public void visit(Sergeant sergeant) {
LOGGER.info("Hello {}", sergeant);
}
@Override
public void visit(Commander commander) {
// Do nothing
}
}
@Slf4j
public class SoldierVisitor implements UnitVisitor {
@Override
public void visit(Soldier soldier) {
LOGGER.info("Greetings {}", soldier);
}
@Override
public void visit(Sergeant sergeant) {
// Do nothing
}
@Override
public void visit(Commander commander) {
// Do nothing
}
}
Por último, podemos mostrar el poder de los visitantes en acción.
commander.accept(new SoldierVisitor());
commander.accept(new SergeantVisitor());
commander.accept(new CommanderVisitor());
Salida del programa:
Greetings soldier
Greetings soldier
Greetings soldier
Greetings soldier
Greetings soldier
Greetings soldier
Hello sergeant
Hello sergeant
Good to see you commander
Diagrama de clases
Aplicabilidad
Utilice el patrón Visitor cuando
- Una estructura de objetos contiene muchas clases de objetos con diferentes interfaces, y desea realizar operaciones en estos objetos que dependen de sus clases concretas.
- Es necesario realizar muchas operaciones distintas y no relacionadas en los objetos de una estructura de objetos, y se desea evitar "contaminar" sus clases con estas operaciones. Visitor permite mantener juntas las operaciones relacionadas definiéndolas en una clase. Cuando la estructura de objetos es compartida por muchas aplicaciones, utilice Visitor para poner las operaciones sólo en aquellas aplicaciones que las necesiten.
- Las clases que definen la estructura del objeto raramente cambian, pero a menudo se quieren definir nuevas operaciones sobre la estructura. Cambiar las clases de la estructura del objeto requiere redefinir la interfaz para todos los visitantes, lo que es potencialmente costoso. Si las clases de la estructura del objeto cambian a menudo, probablemente sea mejor definir las operaciones en esas clases.
Tutoriales
Usos conocidos
- Apache Wicket component tree, Mire MarkupContainer
- javax.lang.model.element.AnnotationValue y AnnotationValueVisitor
- javax.lang.model.element.Element y Element Visitor
- java.nio.file.FileVisitor