Java 中的访问者模式:在不同的对象结构中实现健壮的操作
大约 4 分钟
访问者设计模式的意图
表示对对象结构元素执行的操作。访问者允许您定义新操作,而无需更改其上操作的元素的类。
访问者模式的详细解释以及现实世界示例
现实世界示例
访问者设计模式的类似现实世界示例可以在博物馆导游系统中看到。想象一个博物馆,游客可以参加导游,了解不同类型的展品,例如绘画、雕塑和历史文物。每种展品类型都需要不同的解释,由专门的导游提供。
在这种情况下,展品就像访问者模式中的元素,导游就像访问者。博物馆结构保持不变,但可以添加新的导游和新的导游类型(操作),而无需修改展品本身。每个导游(访问者)实现与展品交互的特定方式,根据其专业领域提供详细的解释,从而将操作与它们操作的对象分离。
通俗地说
Java 访问者模式定义了可以在各种数据结构的节点上执行的操作,增强了 Java 应用程序的可扩展性。
维基百科说
在面向对象编程和软件工程中,访问者设计模式是一种将算法与其操作的对象结构分离的方法。这种分离的实际结果是能够为现有对象结构添加新操作,而无需修改结构。
Java 中访问者模式的编程示例
考虑一个包含军队单位的树结构。指挥官有两个下属中士,每个中士有三个下属士兵。假设层次结构实现了访问者模式,我们可以轻松创建新的对象来与指挥官、中士、士兵或所有这些对象交互。
鉴于上述军队单位示例,我们首先有 Unit
和 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);
}
然后我们有具体的单位 Commander
、Sergeant
和 Soldier
。
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";
}
}
以下是具体的访问者 CommanderVisitor
、SergeantVisitor
和 SoldierVisitor
。
@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
}
}
最后,我们可以展示访问者在行动中的强大功能。
public static void main(String[] args) {
var commander = new Commander(
new Sergeant(new Soldier(), new Soldier(), new Soldier()),
new Sergeant(new Soldier(), new Soldier(), new Soldier())
);
commander.accept(new SoldierVisitor());
commander.accept(new SergeantVisitor());
commander.accept(new CommanderVisitor());
}
程序输出
14:58:06.115 [main] INFO com.iluwatar.visitor.SoldierVisitor -- Greetings soldier
14:58:06.118 [main] INFO com.iluwatar.visitor.SoldierVisitor -- Greetings soldier
14:58:06.118 [main] INFO com.iluwatar.visitor.SoldierVisitor -- Greetings soldier
14:58:06.118 [main] INFO com.iluwatar.visitor.SoldierVisitor -- Greetings soldier
14:58:06.118 [main] INFO com.iluwatar.visitor.SoldierVisitor -- Greetings soldier
14:58:06.118 [main] INFO com.iluwatar.visitor.SoldierVisitor -- Greetings soldier
14:58:06.118 [main] INFO com.iluwatar.visitor.SergeantVisitor -- Hello sergeant
14:58:06.118 [main] INFO com.iluwatar.visitor.SergeantVisitor -- Hello sergeant
14:58:06.118 [main] INFO com.iluwatar.visitor.CommanderVisitor -- Good to see you commander
访问者模式的详细解释以及现实世界示例
何时在 Java 中使用访问者模式
在以下情况下使用访问者模式
- 在需要跨组类似对象高效执行操作,而无需修改其类,并且希望避免在这些类中添加此操作时,在 Java 中实现访问者设计模式。
- 当类结构稳定,但需要对结构执行新操作而无需更改它时使用它。
- 当类集固定,只需要扩展操作时,它很有用。
访问者模式 Java 教程
Java 中访问者模式的现实世界应用
- 编译器设计,其中访问者模式可用于执行诸如漂亮打印、语义检查等操作。
- 抽象语法树 (AST) 处理。
- 文档结构处理(例如,HTML、XML)。
- Apache Wicket 组件树,请参阅 MarkupContainer
- javax.lang.model.element.AnnotationValue 和 AnnotationValueVisitor
- javax.lang.model.element.Element 和 Element Visitor
- java.nio.file.FileVisitor
访问者模式的优点和权衡
优点
- 简化添加新操作:添加新操作很简单,因为您可以添加新的访问者,而无需更改现有代码。
- 单一职责原则:访问者模式允许您将相关行为移至一个类中。
- 开闭原则:元素可以保持封闭以进行修改,而访问者可以进行扩展。
权衡
- 添加新的元素类:如果需要添加新的元素类型,则需要更改访问者接口及其所有具体访问者。
- 循环依赖:在复杂的系统中,这种模式可能会在访问者和元素类之间引入循环依赖。
- 打破封装:访问者模式要求元素类公开足够的信息以允许访问者完成其工作,这可能会打破封装。
相关的 Java 设计模式
- 组合:访问者模式通常与组合模式一起使用,其中访问者可以在组合结构上执行操作。
- 解释器:访问者可用于实现解释器模式中的非终端表达式。
- 策略:访问者可以被认为是一种使策略适用于它们没有设计为操作的对象的方法。