Skip to main content
5 of 5
added 307 characters in body
Doc Brown
  • 220.3k
  • 35
  • 410
  • 623

Here is a straightforward solution, expressing the case literally: implement ElementA.accept in terms of the base class Visitor, and ElementB.accept in terms of Visitor2:

public ElementA implements Visitable {
    public void accept(Visitor v) {
        //...
    }
}

// no derivation from Visitable here, which would require
// to provide accept(Visitor v) 
public ElementB { 
    public void accept(Visitor2 v) {
        //...
    }
}

This avoids the issue of two very similar ElementA.accept methods, which leads to the extendability problem mentioned in your question.

However, this solution will not work if the intention is to have some generic code which calls visitable.accept(someVisitor) without knowing beforehand if visitable is of type ElementA or ElementB (both derived from Visitable), and also not knowing if someVisitor is of type Visitor1 or Visitor2 (both derived from Visitor) . If that's the case, there is no way to prevent getting a combination of objects Visitor1 with ElementB at runtime without loosing the genericity. Hence you need to decide how you want the calling code to behave in case someone passes the forbidden combination in there:

  • maybe you want the behaviour from solution 1, where the forbidden combination throws an exception (so the caller can react accordingly)

  • maybe you want nothing to happen - similar to solution 1, but with Vistor1.visit(ElementB element) implemented empty (so the caller does not have to care for)

  • or maybe you don't need any generic caller. Then you can give up the genericity, make one function which calls ElementA.accept with arbitrary visitors, and a second one which calls ElementB.accept only with visitors of type Visitor2 (using my suggestion from above).

So look at the requirements of the caller - ask yourself: how will you are going to use the visitors (why do you need them at all), and which degree of genericity are you trying to achieve?

Doc Brown
  • 220.3k
  • 35
  • 410
  • 623