我正在查看访问者模式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
方法?
据我所知,编译器了解visit
使用visitor
执行哪个accept
方法,因为它可以匹配传递给this
的{{1}}类型这里的方法:
visitor.visit(this)
修改
刚刚发现除了这里的好答案this answer之外,还提供了很多有用的细节。
答案 0 :(得分:4)
我是否正确地认为这不会起作用,因为编译器不知道访问者执行哪种重载访问方法?
绝对。访问者是双重发送; accept
执行调度的第一站,因为它在item
上是虚拟的。 accept
内的代码通过让编译器选择正确的重载来执行调度的第二段。
据我所知,编译器了解哪个访问方法在访问者上执行,因为它可以匹配传递给
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);
答案 1 :(得分:2)
从JavaScript开发人员的角度来看,accept方法似乎是多余的,因为代码可以这样编写:
for(Visitable item: items) { // directly call visitor passing an item instead of doing so through `accept` method visitor.visit(item); }
代码无法像JavaScript那样编写。要了解原因,让我们先来看看访客的样子。在Java中,它看起来像这样:
void visit(Book book) { ... }
void visit(OtherThing otherThing) {...}
JavaScript没有这样的重载,因此不同的访问方法需要不同的名称。所以它看起来会像这样:
function visitBook(book) { ... }
function visitOtherThing(otherThing) { ... }
现在你显然无法visitor.visit(item)
,因为没有visit
方法。 visitBook
和visitOtherThing
有accept
和visitBook
,但您不知道要拨打哪个,因为您没有使用哪种类型的项目。所以你仍然需要一个接受方法。然后,图书的OtherThing
方法会调用accept
,而visitOtherThing
的{{1}}方法会调用{{1}}。
事实上,这就是JavaScript中的访问者模式通常的样子。
我是否正确地认为这不起作用,因为编译器不知道访问者的哪个重载访问方法要执行?
是
据我所知,编译器了解在访问者上使用accept执行哪种访问方法,因为它可以匹配传递给visitor.visit(this)方法的类型
完全。