3

次の使用例は、リフレクションの正当性と見なされますか?

さまざまなレスポンスを表す XSD (現在プロジェクトにある数百) から生成されたクラスが多数あります。

これらのすべての応答には、拡張するのではなく、共通の応答データ構造が含まれています。

タイムアウトなどのイベントが発生した場合、単一の文字列を特定の値に設定するだけで済みます。

これらのクラスが共通の応答構造を拡張している場合、この応答コードをリフレクションなしで常に設定できますが、そうではありません。

したがって、リフレクションを使用して文字列フィールドのセッターメソッドを取得し、定義済みの値で呼び出すサービス用の簡単なユーティリティを作成しました。私にとって唯一の既知の代替手段は、タイムアウトを処理するコードを複製するクラス固有のメソッドを持つことであり、返される Response クラスの唯一の違いがあります。

protected T handleTimeout(Class<T> timeoutClass) {
    try {
        T timeout = timeoutClass.newInstance();
        Method setCode = timeoutClass.getDeclaredMethod(SET_RESPONSE_CODE, String.class);
        setCode.invoke(timeout, Response.TIMEOUT.getCode());
        return timeout;
    } catch (InstantiationException | IllegalAccessException  | SecurityException | NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
        throw new RuntimeException("Response classes must have field: \"code\" !");
    }

}

関連する事実:

  • この setter メソッドは、何百ものインターフェイスの再作成が必要になるため、決して変更しないでください。

私が見逃したいくつかの落とし穴があるかどうか、または同じ結果を達成するリフレクションの代替ソリューションがあるかどうか、誰かが指摘できますか?

編集: XSD で変更を行う権限がないため、解決策はローカルで行う必要があります。このようなオブジェクトはコンポーネント間で共有されるため、シリアル化に問題はありません。

4

5 に答える 5

4

まず、@kutschkem が提案する標準的な通常の日常的なソリューションがあります。具体的には、この 1 つのセッター メソッドのみを含むインターフェイスを宣言し、それを必要とするすべてのクラスにそのインターフェイスを実装します。これは、標準的なポリモーフィズムを使用して、まさに必要なことを行います。

これには多くのクラスの定義を変更する必要があることを理解しています(ただし、変更は簡単です-「implements MytimeoutThing」をすべてのクラスに追加するだけです)-数千のクラスの場合でも、これはかなり簡単に修正できるようです。

リフレクションには本当の問題があると思います:

  1. サポートする必要があるすべてのクラスへの秘密のインターフェイスを作成していますが、このインターフェイスには契約がありません。新しい開発者が新しいクラスを追加したい場合、このメソッドの名前と署名について魔法のように知っている必要があります。コンパイラがこのコントラクトを認識していないため、実行時にコードが失敗するのは間違いです。(したがって、セッター名のスペルミスのような単純なものは、コンパイラーによって検出されません)

  2. それは醜く、隠され、ソフトウェアの特定の部分の明確な一部ではありません。これらのクラスのいずれかを維持している開発者は、この関数 (セッター) が呼び出されていないことに気づき、削除するだけです。プロジェクトの残りのコードではそのセッターを参照していないため、明らかに必要ありません。

  3. 多くの静的分析ツールは機能しません。たとえば、ほとんどの IDE では、特定の関数が呼び出されるすべての場所と、特定の関数が呼び出すすべての場所を確立できます。反射。ほぼ同一のクラスが何百もあるプロジェクトでは、この機能を失いたくありません。

于 2015-06-02T12:12:41.990 に答える
3

あなたが直面している実際の問題は、共通の抽象化 (同じクラスを継承するか、同じインターフェースを実装する) を共有する必要がある多くのクラスがあることですが、そうではありません。そのように維持しようとして設計を回避することは、基本的に原因ではなく症状に対処することになり、将来さらに多くの問題を引き起こす可能性があります.

代わりに、生成されたすべてのクラスに共通のインターフェース/スーパークラスを持たせることで、根本的な原因を解決することをお勧めします。これを手動で行う必要はありません。これらはすべて生成されているため、苦労せずに自動的に変更できるはずです。

于 2015-06-02T12:09:18.227 に答える
1

さて、次の生成されたコードがあるとしましょう。

public class Response1 {
   public void setResponseCode(int code) {...}
}

public class Response2 {
   public void setResponseCode(int code) {...}
}

次に行う必要があるのは、インターフェイスを作成することです。

public interface ResponseCodeAware { //sorry for the poor name
   public void setResponseCode(int code);
} 

implements ResponseCodeAware次に、生成されたすべてのコード ファイルを調べ、すべてのクラス定義の後に単純に追加するスクリプトを作成する必要があります。(これは、まだ実装されているインターフェースがないことを前提としています。その場合、文字列処理を少しいじる必要があります。)

したがって、生成および後処理されたクラスは次のようになります。

public class Response1 implements ResponseCodeAware {
   public void setResponseCode(int code) {...}
}

public class Response2 implements ResponseCodeAware {
   public void setResponseCode(int code) {...}
}

他に何も変更されていないことに注意してください。そのため、インターフェイス (シリアル化を含む) を認識しないコードはまったく同じように動作するはずです。

最後に、メソッドを次のように書き直します。

protected T handleTimeout(Class<T extends ResponseCodeAware> timeoutClass) {
    try {
        T timeout = timeoutClass.newInstance();
        timeout.setResponseCode( Response.TIMEOUT.getCode() );
        return timeout;
    } catch (InstantiationException | IllegalAccessException  | SecurityException | NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
        throw new RuntimeException("Response class couldn't be instantiated.");
    }
}

ご覧のとおり、残念ながら、オブジェクトを作成するためにリフレクションを使用する必要があり、何らかの種類のファクトリも作成しない限り、そのままになります。ただし、コード生成はここでも役立ちます。ファクトリ クラスを構築すると同時に、クラスをインターフェイスでマークすることができます。

于 2015-06-02T12:39:28.890 に答える