3

I have two parallel hierarchies:

  • B and C implement interface A
  • D and E extend the abstract class F

I want to be able to do: F f = createObject(A a)

where createObject should follow the behaviour:

F f createObject(A a){
    if (a instanceof B)
       return new D(a)
    else if (a instanceof C)
       return new E(a)
    else
        throw some error

That is, the creation of F is dependent on the type of A.

I cannot remake the code to remove this parallel hierarchy dependency. I have tried to find some clean solution to this problem that does not involve instanceof, but haven't been able to.

Any pointers appreciated.

EDIT:

B and C shouldn't have knowledge about D and E, preferably, as the relationship is that D and E has-an A, and A exists independently of D and E.

5
  • When do you create the A? Is that right before calling this method? Perhaps it's possible to handle creating the correct "pairs" at the same time when you know which one you want.
    – Imus
    Apr 18, 2018 at 7:20
  • If I understand right: D and E are providing extra behaviour for B and C respectively. Why can't you just put that extra behaviour into B and C? Edit: to clarify, I think the correct answer here is to refactor the code to avoid this requirement... Unless there is some other reason it can't be refactored? Apr 18, 2018 at 7:22
  • One can say that D and E represent two different algorithms. They accept an interface A as argument, which they process. Depending on the type of A, we want either D or E to be used. B and C are also used for other purposes, so to mix them up with D and E doesn't make sense.
    – andfor
    Apr 18, 2018 at 7:25
  • Imus: unfortunately I don't have control over when A is created. My task is to write D/E/F, which may then be called externally with A by others.
    – andfor
    Apr 18, 2018 at 7:26
  • Maybe_Factor: I can add methods to A/B/C if I want to, but I cannot refactor the structure as they are used for other purposes as well.
    – andfor
    Apr 18, 2018 at 7:43

1 Answer 1

1

I struggled with this problem myself. It seems, the only reasonable solution is to use the Visitor Pattern. This avoids instanceof, and also the need to make instantiation the responsibility of the source type A. However, the source type hierarchy will need to be updated to support the visitor infrastructure.

First, a Visitor interface:

interface AVisitor<Result> {
  Result visitB(B object);
  Result visitC(C object);
}

Next, we need to update the A hierarchy to support the visitor:

interface A {
  <Result> Result accept(AVisitor<Result> visitor);
  ...
}

class B implements A {
  @Override
  public <Result> Result acccept(AVisitor<Result> visitor) {
    return visitor.visitB(this);
  }
  ...
}

class C implements A {
  @Override
  public <Result> Result acccept(AVisitor<Result> visitor) {
    return visitor.visitC(this);
  }
  ...
}

Now, we can implement a visitor that manages object creation from the F hierarchy:

class CreateFFromA implements AVisitor<F> {
  @Override
  public F visitB(B b) { return new D(B); }

  @Override
  public F visitC(C c) { return new E(c); }
}

Finally, we can tie this together in a static helper function:

static F createObjectA(A a) {
  return a.accept(new CreateFFromA());
}

This is a lot of code and complexity to solve this problem. An instanceof is far more convenient. So a visitor-based solution is particularly applicable when

  • the two class hierarchies must be decoupled at all costs,
  • or if there are multiple output type hierarchies, which you want to select at runtime,
  • or if you want to be able to statically check that the function handles all known A subclasses.

There is also a substantial restriction: You cannot add new classes to the A hierarchy with either the visitor- or instanceof-based solution. You would have to add the new type to the visitor interface, and then update all visitor implementations to handle that case. If you need this kind of flexibility, you might have to accept some coupling and use a normal A.create() instance method that subclasses can override.

1
  • Thank you very much for your substantial answer and the great example. As you say, something like a Visitor seems to be the only alternative to instanceof in this case. Answer accepted.
    – andfor
    Apr 18, 2018 at 8:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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