1

私の出発点は以下の通りです:
- 私は渡された引数の型によって異なった振る舞いをするメソッドを持っています
- これらの引数はすべて同じインターフェイスを実装しています。Xすべての引数が同じインターフェイスを実装するオーバーロードされたメソッドを呼び出す

私はこの変換メソッドをすべて実装するさまざまなオブジェクトに適用したいと思いますXインタフェース。

私が思い描いたのは、トランスフォームの関連バリアントを適用する前に各オブジェクトのインスタンスをチェックするtransform(X x1、X x2)を実装することでした。

このコードは動作しますが、コードは醜いようですが、これらのさまざまなインスタンスとキャストを評価するためのパフォーマンスオーバーヘッドも懸念しています。その変換はJavaでできるか、同じ動作を達成するためのよりエレガントで効率的な方法ですか?

以下は、BAを印刷する簡単で実用的な例です。私はそのコードを改善する方法の例を探しています。私の実際のコードでは、当然「変換」の実装が増えていますが、以下のような簡単な実装はありません。

public class A implements X { 
} 

public class B implements X { 
} 

interface X { 
} 

public A transform(A a1, A a2) { 
    System.out.print("A"); 
    return a2; 
} 

public A transform(A a1, B b) { 
    System.out.print("B"); 
    return a1; 
} 

// Isn't there something better than the code below??? 
public X transform(X x1, X x2) { 
    if ((x1 instanceof A) && (x2 instanceof A)) { 
    return transform((A) x1, (A) x2); 
    } else if ((x1 instanceof A) && (x2 instanceof B)) { 
    return transform((A) x1, (B) x2); 
    } else { 
    throw new RuntimeException("Transform not implemented for " 
      + x1.getClass() + "," + x2.getClass()); 
    } 
} 

@Test 
public void trivial() { 
    X x1 = new A(); 
    X x2 = new B(); 
    X result = transform(x1, x2); 
    transform(x1, result); 
} 
+0

関連サイトhttp://sites.google.com/site/steveyegge2/when-polymorphism-fails – double07

+1

パフォーマンスを心配する必要はありません。コードのセクションを非常に頻繁に実行する予定がある場合を除き、パフォーマンスは心配しないでください。最初に動作させ、後で最適化します。 – Bombe

答えて

5

開始点としてVisitor patternをご覧ください。

階層が大きく変更された場合、訪問者のパターンによって変更が広がります。その場合は、acyclic visitorもご覧ください。

コードは次のようになります。

public interface X { 
    void accept(XVisitor v); 
} 

public interface XVisitor { 
    void visit(A a); 
    void visit(B b); 
} 

public class A implements X { 
    public void accept(XVisitor v) { 
    v.visit(this); 
    } 
} 

public class B implements X { 
    public void accept(XVisitor v) { 
    v.visit(this); 
    } 
} 

そして、あなたのアルゴリズムは、このクラスに入る:

public class XTransformerVisitor implements XVisitor { 
    private X result; 
    private A first; 
    public void visit(A a) { 
    if (first == null) first = a; 
    else result = a; 
    } 
    public void visit(B b) { 
    if (first == null) throw new RuntimeException(); 
    result = first; 
    } 
    public X transform(X x1, X x2) { 
    x1.accept(this); 
    x2.accept(this); 
    return result; 
    } 
} 
+0

答えをありがとう。私の階層は変わりません。私は、コードの読みやすさと、instanceofを呼び出してキャストを実行することによるパフォーマンスの影響についてより懸念しています。あなたが私に指摘した例を読んだ後、私の場合、訪問者のパターンがより良いトレードオフになるかどうかはまだ分かりません。それ? – double07

+0

多分、それは始まりです...私はいくつかのコードを掲示し、あなたは裁判官になるでしょう。 –

+0

さて、私は売られています。そして例のおかげで:はるかに具体的にしました。 – double07

3

あなたが探している用語が複数の発送です、です複数の引数の型で多態的な仮想関数の一般化。 JavaやC++を含むほとんどのプログラミング言語は、複数のディスパッチをサポートしていないので、それをエミュレートするためにいくつかの種類のハッカーが必要です。 1つの選択肢は、あなたが上記のもののようなコードを持つことです。もう1つは何かを使用することですlike this。一般的な解決策の1つは、複雑さを抽象化するのに役立つvisitor patternというイディオムを使用することです。

+0

+1のための+1 _multiple dispatch_ –

0

もう少し研究を重ねると、私は反射という概念を見つけました。私はこれが少なくともここでの特定の問題を解決するために、訪問者のパターンよりはるかに簡単だと思います。

上記の私の元のコードはまったく同じ滞在することができますし、迷惑な方法は、変換(X×1を、X×2)は、単純に次のようになります。

public X transform(X x1, X x2) { 
    Method m; 
    try { 
    m = getClass().getMethod("transform", 
      new Class[]{x1.getClass(), x2.getClass()}); 
    return (X) m.invoke(this, new Object[]{x1, x2}); 
    } catch (Exception e) { 
    throw new RuntimeException("Transform not implemented for " 
     + x1.getClass() + "," + x2.getClass()); 
    } 
} 

利点:
- ネストされたのinstanceofテストとキャストIを取り除きます
- すべてのオペランドがダブルディスパッチ/ビジターパターンのアプローチによって実装されなければならない受け入れメソッドを実装する必要はありません。

+0

+1あなたはあなたのデザインを変更する必要はありません... –

関連する問題