Visitor
大约 3 分钟
目的
表示要在对象结构的元素上执行的操作。访问者可让你定义新操作,而无需更改其所操作元素的类。
解释
真实世界例子
考虑有一个带有军队单位的树形结构。指挥官下有两名中士,每名中士下有三名士兵。基于这个层级结构实现访问者模式,我们可以轻松创建与指挥官,中士,士兵或所有人员互动的新对象
通俗的说
访问者模式定义可以在数据结构的节点上执行的操作。
维基百科说
在面向对象的程序设计和软件工程中,访问者设计模式是一种将算法与操作对象的结构分离的方法。这种分离的实际结果是能够在不修改结构的情况下向现有对象结构添加新操作。
程序示例
使用上面的军队单元的例子,我们首先由单位和单位访问器类型。
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);
}
然后我们有具体的单元。
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";
}
}
然后有一些具体的访问者。
public class CommanderVisitor implements UnitVisitor {
private static final Logger LOGGER = LoggerFactory.getLogger(CommanderVisitor.class);
@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);
}
}
public class SergeantVisitor implements UnitVisitor {
private static final Logger LOGGER = LoggerFactory.getLogger(SergeantVisitor.class);
@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
}
}
public class SoldierVisitor implements UnitVisitor {
private static final Logger LOGGER = LoggerFactory.getLogger(SoldierVisitor.class);
@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
}
}
最后,来看看实践中访问者模式的力量。
commander.accept(new SoldierVisitor());
commander.accept(new SergeantVisitor());
commander.accept(new CommanderVisitor());
程序输出:
Greetings soldier
Greetings soldier
Greetings soldier
Greetings soldier
Greetings soldier
Greetings soldier
Hello sergeant
Hello sergeant
Good to see you commander
Class diagram
适用性
使用访问者模式当
- 对象结构包含许多具有不同接口的对象类,并且你希望根据这些对象的具体类对这些对象执行操作。
- 需要对对象结构中的对象执行许多不同且不相关的操作,并且你想避免使用这些操作“污染”它们的类。 访问者可以通过在一个类中定义相关操作来将它们保持在一起。当许多应用程序共享对象结构时,请使用访问者模式将操作仅放在需要它们的那些应用程序中
- 定义对象结构的类很少变化,但是你经常想在结构上定义新的操作。更改对象结构类需要重新定义所有访问者的接口,这可能会导致成本高昂。如果对象结构类经常更改,则最好在这些类中定义操作。
真实例子
- Apache Wicket component tree, see MarkupContainer
- javax.lang.model.element.AnnotationValue and AnnotationValueVisitor
- javax.lang.model.element.Element and Element Visitor
- java.nio.file.FileVisitor