1

訪問者パターンを生成して実装するために助けが必要です。私たちは大量に使用しinstanceofていますが、それは苦痛です。変更できると確信していますが、それを行う方法がわかりません。

基本的に私たちはインターフェースを持っていますProcessData

public interface ProcessData {
  public setDelegate(Object delegate);
  public Object getDelegate();
  //I am sure these delegate methods can use generics somehow
}

ProcessDataGenericこれで、実装するクラスができましたProcessData

public class ProcessDataGeneric implements ProcessData {
  private Object delegate;

  public ProcessDataGeneric(Object delegate) {
    this.delegate = delegate;
  }
}

ProcessData を取得する新しいインターフェイス

interface ProcessDataWrapper {
  public ProcessData unwrap();
}

ProcessData を取得できるようにラッパーを実装する一般的な抽象クラスになりました

@XmlSeeAlso( { ProcessDataMotorferdsel.class,ProcessDataTilskudd.class })
public abstract class ProcessDataCommon implements ProcessDataWrapper {
  protected ProcessData unwrapped;

  public ProcessData unwrap() {
    return unwrapped;
  }
}

これで実装

public class ProcessDataMotorferdsel extends ProcessDataCommon {

  public ProcessDataMotorferdsel() {
    unwrapped = new ProcessDataGeneric(this);
  }
}

同様に

public class ProcessDataTilskudd extends ProcessDataCommon {

  public ProcessDataTilskudd() {
    unwrapped = new ProcessDataGeneric(this);
  }
}

これらのクラスを使用するときは、常に行う必要がありますinstanceof

ProcessDataCommon pdc = null;
if(processData.getDelegate() instanceof ProcessDataMotorferdsel) {
   pdc = (ProcessDataMotorferdsel) processData.getDelegate();
} else if(processData.getDelegate() instanceof ProcessDataTilskudd) {
   pdc = (ProcessDataTilskudd) processData.getDelegate();
}

これを行うためのより良い方法があることは知っていますが、ジェネリックとビジター パターンを利用する方法がわかりません。どんな助けでも大歓迎です。

アップデート

これらのクラスは、はるかに大きな実装の一部にすぎないことを付け加えておきます。ProcessDataandProcessDataGenericは、デリゲート (など) の外側にあるものですProcessDataMotorferdsel。デリゲートはすべて拡張しProcessDataCommonます。

おそらくリファクタリングを行うのが最善であることに同意できますが、これは 2 年前の製品コードであり、リファクタリングにはコストがかかります (時間、テストなど)。しかし、私は喜んでそれを行います。

更新 #2

Generic プロセスを開始しようとしましたが、コンパイル エラーが発生します。これが今の様子です。

public interface ProcessData<T extends ProcessDataCommon> {
  T getDelegate();
  setDelegate(T delegate);
}

public class ProcessDataGeneric<T extends ProcessDataCommon> implements ProcessData<T> {
  private T delegate;
  //Getter & setter
  public ProcessDataGeneric(T delegate) {
    this.delegate = delegate;
  }
}

public class ProcessDataMotorferdsel extends ProcessDataCommon {
  public ProcessDataMotorferdsel() {
    unwrapped = new ProcessDataGeneric<ProcessDataMotorferdsel>(this);
  }
}

オンラインでコンパイルエラーが発生しますunwrapped = new ProcessDataGeneric<ProcessDataMotorferdsel>(this);

[javac] ProcessDataMotorferdsel.java:52: incompatible types [javac] found : ProcessDataGeneric<ProcessDataMotorferdsel> [javac] required: ProcessData<ProcessDataCommon> [javac]

そのエラーメッセージの表も裏もわかりません。ProcessDataMotorferdsel クラスは ProcessDataCommon を拡張するため、IMO は機能するはずです。

4

4 に答える 4

1

この答えはおそらく単純すぎると思いますが、理想的なソリューションでは、質問のコードのほぼすべてが削除されると思います。質問に答えようとする人々にとっての問題は、コードが解決しようとしている実際の問題が、残っているものからは明らかでないことです。

instanceof をリファクタリングするための一般的なアプローチは、サブクラスを「教えない」スタイルのインターフェイスと組み合わせて使用​​することです。次のように、ProcessDataGeneric にタスク全体を実行するように指示できる場合は、ProcessDataGeneric にデリゲートを依頼する必要はありません。

public interface ProcessData {
    public <T> T process(Data data);
}

public class ProcessDataGeneric implements ProcessData {
    private ProcessData delegate;

    public ProcessDataGeneric(ProcessData delegate) {
        this.delegate = delegate;
    }

    public <T> T process(Data data) {
        return delegate.process(data);
}

実際の ProcessData サブクラスを保持するだけなので、ProcessDataGeneric が本当に必要かどうかさえわかりません。

public class ProcessDataMotorferdsel implements ProcessData {

    // Process the data the Motorferdsel way.
    public <T> T process(Data data) { ... }
}

public class ProcessDataTilskudd implements ProcessData {

    // Process the data the Tilskudd way.
    public <T> T process(Data data) { ... }
}

...そして、次のようにサブクラスを使用できます。

ProcessData processor = new ProcessDataMotorferdsel();
Result      result    = processor.process(data);

...デリゲートとそのタイプについて心配する必要はありません。

特に正しいサブクラスを計算する必要がある場合は、コンストラクターの代わりにファクトリ クラスを使用してサブクラスのインスタンスを取得する方がよい場合がよくあります。

于 2010-05-28T08:58:44.413 に答える
0

オブジェクトが目的のターゲット クラスを既に拡張している場合は、キャストする必要はありません。つまり、これを行うことができます:

public interface ProcessData {
    public void setDelegate(ProcessDataCommon delegate);
    public ProcessDataCommon getDelegate();
}

この:

public class ProcessDataGeneric implements ProcessData {
    private ProcessDataCommon delegate;
    public ProcessDataGeneric(ProcessDataCommon delegate) {
        this.delegate = delegate;
    }
    @Override
    public ProcessDataCommon getDelegate() {
        return delegate;
    }
    @Override
    public void setDelegate(ProcessDataCommon delegate) {
        this.delegate = delegate;
    }
}

そして、インスタンスの櫛は次のように単純化されます。

ProcessDataCommon pdc = processData.getDelegate();
于 2010-05-28T15:24:55.963 に答える
0

私も何かが足りないのかもしれませんが、

ProcessDataCommon pdc = null;
if(processData.getDelegate() instanceof ProcessDataCommon) {
   pdc = (ProcessDataCommon) processData.getDelegate();
}

同等である必要があります..?おっしゃるとおり、デリゲートは常にProcessDataCommon型です。

ProcessData#getDelegate()が返される場合はProcessDataCommon、残りのinstanceofチェックも削除できます。

于 2010-05-28T08:54:30.287 に答える
0

私はそれを機能させました。

UPDATE #2を見て、この変更を含めます。

public abstract class ProcessDataCommon<T extends ProcessDataCommon<?>> implements ProcessDataWrapper {
}

すべてをコンパイルしました。

于 2010-05-31T08:48:07.783 に答える