I have a composite object (I will call it A) that has fixed number of sons (I will call them A1 and A2)

All of them have an Accept(Visitor) method.

I have a GUI, A is the main window that consists of many sub-windows (a.k.a A1 and A2). The visitors will be visitors for when a user clicks a button to start a new operation such as starts a new game, guesses a word incorrectly and other events. (Command design pattern is used, but this is out of the scope) and when such events happends the visitor will update the GUI appropriately.

When considering how to implement Accept(Visitor) at A, I consider weather I should cascade the Accept(Visitor) request to A1 and A2 as well or not.

Here is my argument for cascading:

More transparency - in all of my visitors until now, I do want to go over the sons of A. but in order to do that without cascading, I will need getters for the sons of A, but the sons of A can change (even at run time, if I change implementation from fixed amount of sons to a collection) and I do not wish to change the interface all the time to support such get operations. You might suggest iterators for that, but the thing is that the Visitor might want to visit A1 and A2, but if another A3 son will be added, he might not want to visit it. So as it iterates through A's sons, it will must resort to ugly type checking, which is what the Visitor design pattern has come to eradicate. Instead, it might be much easier to simply cascade all the requests to the children, and if a visitor isn't interested in visiting some type, it cant just leave the visit method for it empty. Moreover, in the symbolic level, A1 and A2 are part of A, so the cascade makes sense.

Here is my argument against cascading:

Looser coupling - cascading means you force the visitor to visit children it might didn't want to visit, which can lead to unexpected results. Moreover, it might not support accepting the types of the children. If getter or iterators are used, then the visitor can choose weather it wants to go over the children.

What approach should I take?

  • Welcome to SE.SE! I think your question makes a lot of sense, and its answer depend very much on the concrete use case. Having more details on the nature of your composite and what it does, would be needed to make a decision.
    – Helena
    Apr 18 at 16:34
  • @Helena thanks for the feedback! I updated the question to provide context.
    – Aviv Aviv
    Apr 18 at 16:57
  • 1
    The only question you really have to answer here is "How well does my solution satisfy my application's specific requirements?" Answer that, and you'll know which solution to use. Apr 18 at 17:37

There is no single best answer to your question. It’s not without reason that GoF let this question open:

(p.339) Who is responsible for the traversal of the object structure? (...) We can put responsibility for traversal in any of three places: in the object structure, in the visitor, or in a separate iterator object.

Often the object structure is responsible for the iteration. (...) A composite will commonly traverse itself by having each Accept operation traverse the element’s children and call Accept on each of them recursively.

That’s what you call cascading. It’s often used that way, because the object structure best knows its structure and how to traverse it. If you’d give this responsibility to the visitor you might have to replicate the traversal logic in all the concrete visitors (unless you manage to implement Accept using some kind on template method pattern).

Putting the traversal in the visitor is of interest mainly for complex visiting algorithms, when the traversal depends intermediary results of the visiting (GoF gives an example of such situation). But this approach is not looser coupling: it’s stronger coupling because the visitor needs to know more about how to traverse.

If looser coupling is your main concern, you may use the approach you didn’t mention: move the traversal logic into an iterator. Build the visitor in a way it uses this special iterator, and let the visitor decide in which nodes it is interested. But in most of the cases this will lead to very complex code compared to a much simpler and robust cascade.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.