jeudi 24 mars 2016

Multiple Visitors on related classes

I'm simplifying things to point out my basic design problem.

I have an hierarchy like this:

              R                <-- interface
            /   \
           /     \
          /       \
        BR         RR          <-- abstract classes
      / | \       / | \
     /  |  \     /  |  \
  BRA BRB BRC  RRA RRB RRC     <-- classes

where BRA, BRB, BRC, RRA, RRB and RRC are classes that need to be visited.

I also have two visitor classes, who don't share any common ancestor class (for now). So, finally, the code is structured like this:

public interface R {
    /* . . . */
}

public abstract class BR implements R {        
    /* . . . */        
    public abstract void accept(VisitorBR vbr);
}

public abstract class RR implements R {
    /* . . . */
    public abstract void accept(VisitorRR vrr);
}


public class BRA extends BR {
    /* . . . */
    public void accept(VisitorBR vbr) { vbr.visit(this); }
}

public class BRB extends BR {
    /* . . . */
    public void accept(VisitorBR vbr) { vbr.visit(this); }
}

public class BRC extends BR {
    /* . . . */
    public void accept(VisitorBR vbr) { vbr.visit(this); }
}


public class RRA extends RR {
    /* . . . */
    public void accept(VisitorRR vrr) { vrr.visit(this); }
}

public class RRB extends RR {
    /* . . . */
    public void accept(VisitorRR vrr) { vrr.visit(this); }
}

public class RRC extends RR {
    /* . . . */
    public void accept(VisitorRR vrr) { vrr.visit(this); }
}

and

public class VisitorBR {
    /* . . . */
    public void visit(BRA r) { /* . . . */ }
    public void visit(BRB r) { /* . . . */ }
    public void visit(BRC r) { /* . . . */ }
}

public class VisitorRR {
    /* . . . */
    public void visit(RRA r) { /* . . . */ }
    public void visit(RRB r) { /* . . . */ }
    public void visit(RRC r) { /* . . . */ }
}


The client class has a BlockingQueue<R>, and one reference to an object of each visitor class, and needs to handle all elements of the queue using the most suitable visitor. Looks like this:

public class Client implements Runnable {
    private VisitorBR vbr;
    private VisitorRR vrr;
    private BlockingQueue<R> q;

    /* . . . */

    @Override
    void run() {
        for (;;) {
            R r = q.take();

            /*** Somehow handle r with the most suitable visitor, ***/
            /*** based on whether it's descendant of BR or RR. ***/

        }
    }
}

What would be the most elegant solution for this? By the way, in any case, the visitors must not be nested classes.

My workaround is to define public static final enum fields in abstract classes BR and RR to distinguish them, and use an if block, something like this:

@Override
void run() {
    for (;;) {
        R r = q.take();

        if (r.getType() == BR)
            ((BR) r).accept(vbr);
        else // if (r.getType() == RR)
            ((RR) r).accept(vrr)
    }
}

But there has to be a more elegant solution to combine two visitor classes than this.

Aucun commentaire:

Enregistrer un commentaire