1

私の出発点は次のとおりです
。-渡される引数のタイプに応じて異なる動作をするようにオーバーロードしたメソッドtransformがあります(transform(A a1、A a2)およびtransform(A a1、B b)を参照)以下の私の例では)
-これらの引数はすべて同じインターフェイスXを実装しています

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

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

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

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

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

3 に答える 3

5

出発点としてビジターパターンを見てください。

階層が大きく変化する場合、ビジターパターンは変化を全体に広げます。その場合は、非巡回ビジターも見てください。

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

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;
  }
}
于 2011-01-13T02:44:53.420 に答える
3

あなたが探している用語は多重ディスパッチです。これは、複数の引数のタイプでポリモーフィックな仮想関数の一般化です。JavaやC++を含むほとんどのプログラミング言語は、多重ディスパッチをサポートしていないため、それをエミュレートするには何らかのハッカーが必要です。1つのオプションは、上記のようなコードを使用することです。もう1つのオプションは、このようなコードを使用することです。一般的な解決策の1つは、ビジターパターンと呼ばれるイディオムを使用することです。これは、複雑さを抽象化するのに役立ちます。

于 2011-01-13T02:47:12.623 に答える
0

さらに調べてみると、リフレクションの概念に出くわしました。少なくともここでの特定の問題を解決するには、これはビジターパターンよりもはるかに簡単だと思います。

上記の私の元のコードはまったく同じままであり、迷惑なメソッドtransform(X x1、X x2)は単純に次のようになります。

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

利点:
-元の投稿で持っていたテストとキャストのネストされたインスタンスを取り除きます
-ダブルディスパッチ/ビジターパターンアプローチによってもたらされるすべてのオペランドが実装する必要があるacceptメソッドを実装する必要がありません

于 2011-01-20T04:00:56.057 に答える