0

私は次の設定をしています:

class Base {};
class ImplA extends Base {};
class ImplB extends Base {};
class ImplC extends Base {};

Base baseFactory(int type) {
    switch(type) {
    case 0:
        return new ImplA();
    case 1:
        return new ImplB();
    case 2:
        return new ImplC();
}

Base a = baseFactory(0);
Base b = baseFactory(1);
Base c = baseFactory(2);


List<Base> list = new ArrayList<Base>();

list.add(a);
list.add(b);
list.add(c);

// Somewhere else I have:
interface BaseHandler {
    process(ImplA a);
    process(ImplB b);
    process(ImplC c);
};

今、私ができるようにしたいのは、次のようなものです。

class Processor {

BaseHandler bh;

Processor(BaseHandler bh) {
    this.bh = b;
}

void processList(List<Base> list) {

    for (Base x : list) {
        bh.process(x);
    }
}

そして、ユーザーに BaseHandler を実装してもらい、Base リスト内の各要素を操作する Processor を構築できるようにします。

ただし、process(Base)定義されていないため、これは機能しません。3 つの if ステートメントを追加するだけで簡単に思えるかもしれませんが、Base を拡張するクラスのインスタンスを構築する際に、スイッチのような構造を既に持っています。これを何度も繰り返す必要はないようです。リスト内の各 Base のランタイム クラスを決定し、適切なメソッドを呼び出す中間ステップを記述せずに、このアイデアを実現する方法はありますか (実際には別のスイッチ ケースですが、if になります)。

process回避策の 1 つは、Impl クラスによって実装する必要がある抽象メソッドを各 Base に持たせることだと思います。ただし、ユーザーは Impl クラスを実装しないため、これは私の状況では受け入れられません。基本的にprocess、ユーザー定義のコールバックである必要があります。さらに、processが Impl または Base クラスのメンバーであることは意味がありません。これはまったく関係がないためです。これは、呼び出された型に動的に応答する必要がある別のコールバックです。また、型は常に Base のサブクラスであることが保証されています。

4

3 に答える 3

6

説明する「中間ステップ」は必要ですが、ifステートメントである必要はありません。あなたが探しているのは、訪問者パターンを使用した二重発送です。基本的に、Baseクラスにはメソッドがあります:

void accept(BaseHandler handler);

各サブクラスは次のように実装します。

handler.process(this);

wherethisは、コンパイル時に各サブクラスの正しい型に解決されます。

于 2012-07-07T16:05:20.687 に答える
2

あなたが望むのはVisitorパターンのようです:

public interface BaseVisitor {
  void caseA(ImplA a);
  void caseB(ImplB b);
  void caseC(ImplC c);
}

public class MyVisitor implements BaseVisitor {
  void visit(List<Base> bases) {
    for (Base b : bases) {
      b.accept(this);
    }
  }
  public void caseA(ImplA a) { // ... }
  public void caseB(ImplB b) { // ... }
  public void caseC(ImplC c) { // ... }
}

public abstract class Base {
  abstract void accept(BaseVisitor visitor);
}

public class ImplA {
  public void accept(BaseVisitor visitor) {
    visitor.caseA(this);
  }
}
public class ImplB {
  public void accept(BaseVisitor visitor) {
    visitor.caseB(this);
  }
}
public class ImplC {
  public void accept(BaseVisitor visitor) {
    visitor.caseC(this);
  }
}
于 2012-07-07T16:12:19.280 に答える
2

あなたが探しているのは Visitor パターンです。に抽象メソッドを配置しますBaseが、それが行うのは で適切なメソッドを呼び出すことだけですBaseHandler

public interface Base {
    void acceptHandler(BaseHandler handler);
}

次に、具体的な実装がオーバーライドされacceptHandler、正しいオーバーロードが呼び出されます。

public class ImplA implements Base {
    public void acceptHandler(BaseHandler handler) {
        handler.process(this);
    }
}

この時点では、オーバーロードにはあまり価値がなく、メソッドにわかりやすい名前を付けたほうがよいでしょう。

于 2012-07-07T16:07:17.740 に答える