访问者模式中需要什么 accept 方法

What is the need for `accept` method in Visitor pattern

我正在查看访问者模式 here 的解释,其中显示了以下代码:

public class ShoppingCart {
  public double calculatePostage() {
    PostageVisitor visitor = new PostageVisitor();
    for(Visitable item: items) {
      item.accept(visitor);
    }

public class PostageVisitor implements Visitor {
  public void visit(Book book) {

public class Book implements Visitable{
  public void accept(Visitor vistor) {
    visitor.visit(this);
  }

从 JavaScript 开发人员的角度来看,accept 方法似乎是多余的,因为代码 可以 写成这样:

for(Visitable item: items) {
   // directly call visitor passing an item instead of doing so through `accept` method
   visitor.visit(item);  
}

我是否可以假设这不会起作用,因为编译器不知道要执行访问者的哪个重载 visit 方法?

据我所知,编译器知道在 visitoraccept 上执行哪个 visit 方法,因为它可以匹配传递给 [=] 的 this 的类型19=] 方法在这里:

public void accept(Visitor vistor) {
    visitor.visit(this);
}

编辑:

刚刚发现除了这里的精彩答案 this answer 还提供了很多有用的细节。

Am I right to assume that this won't work because the compiler doesn't know which overloaded visit method of the visitor to execute?

当然可以。访客为双派遣accept 执行调度的第一段,因为它在 item 上是虚拟的。 accept 中的代码通过让编译器选择适当的重载来执行分派的第二步。

As I understand the compiler understands which visit method to execute on the visitor with accept since it can match the type of this passed to the visitor.visit(this)

完全正确。我认为访问者的这个实现中令人困惑的部分是过载。如果不重载 visit 而是给每个重载一个单独的名称,则更容易看到发生了什么。换句话说,而不是

public void visit(Book book);
public void visit(Cow cow);
public void visit(Island island);

你写

public void visitBook(Book book);
public void visitCow(Cow cow);
public void visitIsland(Island island);

From the standpoint of JavaScript developer the accept method seems redundant since the code could be written like this:

for(Visitable item: items) {
    // directly call visitor passing an item instead of doing so through `accept` method
    visitor.visit(item);  
}

在Java脚本中不能这样写代码。要知道为什么,让我们先看看访问者的样子。在 Java 中,它看起来像这样:

void visit(Book book) { ... }
void visit(OtherThing otherThing) {...}

Java脚本没有这样的重载,所以不同的访问方法需要不同的名称。所以它看起来像这样:

function visitBook(book) { ... }
function visitOtherThing(otherThing) { ... }

现在你显然不能visitor.visit(item)因为没有visit方法。有 visitBookvisitOtherThing,但您不知道要调用哪个,因为您不知道您拥有的是什么类型的项目。所以你仍然需要一个接受方法。那么一本书的 accept 方法会调用 visitBookOtherThingaccept 方法会调用 visitOtherThing.

实际上,Java脚本中的访问者模式通常是这样的。

Am I right to assume that this won't work because the compiler doesn't know which overloaded visit method of the visitor to execute?

是的。

As I understand the compiler understands which visit method to execute on the visitor with accept since it can match the type of this passed to the visitor.visit(this) method here

完全正确。