5

I'm looking at the explanation of the visitor pattern here which shows the following code:

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);
  }

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);  
}

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:

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

Edit:

Just found that in addition to the great answers here this answer also provides a lot of useful details.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • 1
    Yes, you're right. If you had a unique visit(Visitable) method in the visitor, the visitor would have to make a series of instanceof checks to know what the concrete type of the Visitable is, and be able to access their specific properties and methods. That is exactly what the visitor pattern intends to avoid. – JB Nizet Nov 11 '17 at 17:56
  • @JBNizet, cool, thanks. And as I understand since all items in `ShoppingCart` implement `Visitable` interface and the method is defined as `visit(Visitable)` on the visitor I wouldn't have any problems with the type mismatch during compilation? – Max Koretskyi Nov 11 '17 at 17:59
  • This is generally true for most descriptions of design patterns that are expressed in terms of a statically typed language. In a language that isn't statically typed, you don't need most (or often, any) of the explicit scaffolding. Consider 'array-like' (javascript) or 'file-like' (python) objects. – pvg Nov 11 '17 at 17:59
  • 1
    [this answer](https://stackoverflow.com/a/16629618/2545680) from a different question seems to be relevant as well – Max Koretskyi Nov 11 '17 at 18:02
  • Here's a general discussion of the broader issue by Peter Norvig that's worth a read. https://norvig.com/design-patterns/ – pvg Nov 11 '17 at 18:03
  • @pvg, cool, thanks! – Max Koretskyi Nov 11 '17 at 18:04

2 Answers2

5

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?

Absolutely. Visitor is for double-dispatch; accept executes the first leg of dispatch, because it is virtual on the item. The code inside accept executes the second leg of dispatch by letting the compiler pick the proper overload.

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)

This is exactly right. I think the part that is confusing in this implementation of the visitor is the overload. It is much easier to see what's going on when instead of overloading visit you give each overload a separate name. In other words, instead of

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

you write

public void visitBook(Book book);
public void visitCow(Cow cow);
public void visitIsland(Island island);
avp
  • 4,895
  • 4
  • 28
  • 40
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • thanks! I'll read more about double-dispatch technique. The code I'm looking at (in JavaScript though ) also uses name `visit` for method on the Visitable instead of `accept`. Have you seen that convention before? – Max Koretskyi Nov 11 '17 at 18:05
  • 1
    @AngularInDepth.com the name doesn't matter much, but it's more logical for a host to accept a visitor, and for the visitor to visit a host. – JB Nizet Nov 11 '17 at 18:10
2

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);  
}

The code couldn't be written like that in JavaScript. To see why, let's first look at what the visitor looks like. In Java, it'd look like this:

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

JavaScript doesn't have overloading like this, so the different visit methods would need different names. So it'd would look like this instead:

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

Now you clearly can't do visitor.visit(item) because there is no visit method. There are visitBook and visitOtherThing, but you don't know which of those to call because you don't what type of item you have. So you still need an accept method. Then a book's accept method would call visitBook and an OtherThing's accept method would call visitOtherThing.

This is, in fact, what the visitor pattern in JavaScript usually looks like.

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?

Yes.

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

Exactly.

Community
  • 1
  • 1
sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • great, thanks! although my question was about Java, not JavaScript implementation, your answer still gave me some insights into the code I'm currently looking at in JavaScript. The code I'm looking at also uses name `visit` for method on the Visitable instead of `accept`. Have you seen that convention before? – Max Koretskyi Nov 11 '17 at 18:06
  • @AngularInDepth.com My goal was to make you understand why Java needs the `accept` method, by explaining why JavaScript needs it. It seemed to me that your confusion mainly stemmed from the misconception that the need for `accept` was Java-specific (or at least specific to languages with overloading), so I meant to clarify that. – sepp2k Nov 11 '17 at 18:10
  • "The code I'm looking at also uses name visit for method on the Visitable instead of accept. Have you seen that convention before?" Yes, but I don't like it. The visitor is the one who does the visiting. If anything the `accept` method should be called `byVisitedBy`, but that sounds stupid. – sepp2k Nov 11 '17 at 18:12
  • _My goal was to make yo..._ - I see now, additional thanks then for mixing up JavaScript :). Indeed, the `Visitor` I'm looking now implements 'visitElement', `visitText` and other methods just like you described. But it could be implemented in JS with just one method `visit` that inside does a bunch of `instanceof `checks and then decides what method to call, right? – Max Koretskyi Nov 11 '17 at 18:16
  • @AngularInDepth.com You can do that in Java, too, but then you're no longer implementing the visitor pattern. – sepp2k Nov 11 '17 at 18:26
  • hmm, I thought that the `visitor` pattern emphasize separation of logic from structure, so even with the implementation I mentioned above it still should be visitor. The `accept` part as I understand is just for `double-dispatch` mechanism, no? – Max Koretskyi Nov 11 '17 at 18:44