0

私は基本的な設計上の問題を指摘することを単純化しています。関連クラスの複数訪問者

は、私はこのような階層構造を持っている:BRABRBBRCRRARRBRRCが訪問する必要があるクラスがある

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

私はまた、共通の祖先クラスを共有していない2つのビジタークラスを持っています(今のところ)。だから、最終的には、コードは次のように構成されています

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

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) { /* . . . */ } 
} 

クライアントクラスはBlockingQueue<R>、および各訪問者クラスのオブジェクトへの1つの参照を持っている、とする必要があります最も適切な訪問者を使用してキューのすべての要素を処理します。これは次のようになります:

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. ***/ 

     } 
    } 
} 

これに対して最も洗練されたソリューションは何でしょうか?ところで、どんな場合でも、訪問者はネストされたクラスであってはならないし、私はinstanceofを避けようとしている。

私の回避策は、このような何か、それらを区別するために抽象クラスBRRRpublic static final列挙型フィールドを定義し、ifブロックを使用することです:

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

をしかし結合するために、よりエレガントな解決策が存在しなければなりませんこれより2つのビジタークラス。

+0

私はあなたがダブルディスパッチを探していると思います。 https://sourcemaking.com/design_patterns/visitor/java/2を参照してください。 –

+0

@AdiLevin私はこの情報源を認識していますが、私の場合には調整できませんでした。 – chrk

答えて

0

Visitorパターンの例を示します。ここでは訪問先の種類ごとにvisit()のメソッドVisitorを使用しています。

public class Demo { 
    public static void main (String [] args) { 
     Point p = new Point2d(1, 2); 
     Visitor v = new Chebyshev(); 
     p.accept(v); 
     System.out.println(p.getMetric()); 
    } 
} 

interface Visitor { 
    public void visit (Point2d p); 
    public void visit (Point3d p); 
} 

abstract class Point { 
    public abstract void accept (Visitor v); 
    private double metric = -1; 
    public double getMetric() { 
     return metric; 
    } 
    public void setMetric (double metric) { 
     this.metric = metric; 
    } 
} 

class Point2d extends Point { 
    public Point2d (double x, double y) { 
     this.x = x; 
     this.y = y; 
    } 

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

    private double x; 
    public double getX() { return x; } 

    private double y; 
    public double getY() { return y; } 
} 

class Point3d extends Point { 
    public Point3d (double x, double y, double z) { 
     this.x = x; 
     this.y = y; 
     this.z = z; 
    } 
    public void accept (Visitor v) { 
     v.visit(this); 
    } 

    private double x; 
    public double getX() { return x; } 

    private double y; 
    public double getY() { return y; } 

    private double z; 
    public double getZ() { return z; } 
} 

class Euclid implements Visitor { 
    public void visit (Point2d p) { 
     p.setMetric(Math.sqrt(p.getX()*p.getX() + p.getY()*p.getY())); 
    } 
    public void visit (Point3d p) { 
     p.setMetric(Math.sqrt(p.getX()*p.getX() + p.getY()*p.getY() + p.getZ()*p.getZ())); 
    } 
} 

class Chebyshev implements Visitor { 
    public void visit (Point2d p) { 
     double ax = Math.abs(p.getX()); 
     double ay = Math.abs(p.getY()); 
     p.setMetric(ax>ay ? ax : ay); 
    } 
    public void visit (Point3d p) { 
     double ax = Math.abs(p.getX()); 
     double ay = Math.abs(p.getY()); 
     double az = Math.abs(p.getZ()); 
     double max = ax>ay ? ax : ay; 
     if (max<az) max = az; 
     p.setMetric(max); 
    } 
} 
+0

申し訳ありませんが、私は私の質問でそれを言及すべきでした。 'instanceof'は私がここで避けようとしているものです。それが私が最初にenumフィールドを使用している理由です(私は知っています、それはほぼ同じです)。これを含む質問を編集する。 – chrk

+0

'instanceof'のない新しいソリューションを追加しました –

+0

これは、私が提案している回避策とまったく同じです。単にenumの代わりにStringを使用しています:)このようなものがあれば、もっとエレガントなものを探していました。 – chrk

1

さて、私は今のより良い考え出した何かをしたと思う、Selective Visitor Pattern, as described hereに影響を与えました。しかし、私はまだエレガントなソリューションにオープンしています。

新しいクラスSelectiveVisitorが定義されています。これは、現在Clientが所有する訪問者クラスの唯一の参照です。

public interface R { 
    /* . . . */ 
    public accept(SelectiveVisitor sv); 
} 

ので、BRRRは次のように変更されています:新しいクラスは次のように定義されて

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

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

public class SelectiveVisitor { 
    private VisitorBR vbr; 
    private VisitorRR vrr; 

    public SelectiveVisitor(VisitorBR vbr, VisitorRR vrr) { 
     this.vbr = vbr; 
     this.vrr = vrr; 
    } 

    public void visit(R r) { 
     // this method should never be called in practice 
     // it's here only to satisfy the selective visitor pattern 
     return; 
    } 

    public void visit(BR r) { 
     r.accept(this.vbr); 
    } 

    public void visit(RR r) { 
     r.accept(this.vrr); 
    } 
} 

Clientが今変わるインターフェイスRは現在、追加のメソッドを宣言しています〜へ:

public class Client implements Runnable { 
    private SelectiveVisitor sv; 
    private BlockingQueue<R> q; 

    /* . . . */ 

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

      r.accept(sv); 
     } 
    } 
} 

r.accept(selectiveVisitor)が呼び出されるたびに、SelectiveVisitorクラスのvisit方法はBRRRのサブクラスの一つによって呼び出されます。

visitメソッドがオーバーロードされています。呼び出されるたびに、最も特定のバージョンが動的に選択されるため、最も適切な訪問者はrを訪問します。


私は念の誰かに、将来的に同様の設計上の問題によりつまずき、より良い何もそれまでに提案されていない、ここでこのポストを残しています。

関連する問題